├── .gitmodules ├── .gitignore ├── README.md ├── CalibrationWizard.pro ├── Jenkinsfile ├── SConstruct ├── PolyEval.h ├── Calibrator.h ├── main.cc ├── TestA2DPage.h ├── TreeItem.h ├── TreeModel.h ├── TreeItem.cc ├── CalibrationWizard.h ├── TreeModel.cc ├── AutoCalClient.h ├── Calibrator.cc ├── TestA2DPage.cc ├── CalibrationWizard.cc ├── LICENSE.txt └── AutoCalClient.cc /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "site_scons"] 2 | path = site_scons 3 | url = ../eol_scons 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | moc_*.* 3 | ui_*.h 4 | .sconf_temp 5 | .sconsign.dblite 6 | config.log 7 | auto_cal 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aircraft_auto_cal 2 | EOL/RAF Analog Card Calibration and Diagnostic Tool 3 | 4 | The auto cal program provides two functions. 5 | 6 | 1) calibration of the NCAR A/D card. 7 | 2) diagnostics - where voltages can be feed into A/D cards, this funtionality works for the NCAR A/D and the Diamond card. 8 | -------------------------------------------------------------------------------- /CalibrationWizard.pro: -------------------------------------------------------------------------------- 1 | HEADERS = CalibrationWizard.h 2 | SOURCES = CalibrationWizard.cpp \ 3 | main.cpp 4 | RESOURCES = CalibrationWizard.qrc 5 | 6 | # install 7 | target.path = $$[QT_INSTALL_EXAMPLES]/dialogs/CalibrationWizard 8 | sources.files = $$SOURCES $$HEADERS $$FORMS $$RESOURCES *.pro images 9 | sources.path = $$[QT_INSTALL_EXAMPLES]/dialogs/CalibrationWizard 10 | INSTALLS += target sources 11 | 12 | symbian: include($$QT_SOURCE_TREE/examples/symbianpkgrules.pri) 13 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | node { 4 | label 'CentOS9_x86_64' 5 | } 6 | } 7 | triggers { 8 | pollSCM('H/30 8-18 * * * ') 9 | } 10 | stages { 11 | stage('Build') { 12 | steps { 13 | sh 'scons' 14 | } 15 | } 16 | } 17 | post { 18 | failure { 19 | mail(to: 'cjw@ucar.edu cdewerd@ucar.edu janine@ucar.edu', subject: 'aircraft_auto_cal Jenkinsfile build failed', body: 'aircraft_auto_cal Jenkinsfile build failed') 20 | } 21 | } 22 | options { 23 | buildDiscarder(logRotator(numToKeepStr: '10')) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | # -*- python -*- 2 | ## 2010, Copyright University Corporation for Atmospheric Research 3 | 4 | import os 5 | import eol_scons 6 | 7 | # eol_scons.debug.SetDebug("true") 8 | # 9 | # Don't load tools that perform compiler or pkg-config checks 10 | # until the cross tool is loaded, and PKG_CONFIG_PATH is set. 11 | env = Environment(tools=['default', 'qt5', 'qtwidgets', 'qtcore', 'qtnetwork', 'nidas', 'gsl']) 12 | 13 | opts = eol_scons.GlobalVariables('config.py') 14 | opts.AddVariables(('PREFIX', 15 | 'installation path', 16 | '/opt/nidas', None, eol_scons.PathToAbsolute)) 17 | 18 | opts.Update(env) 19 | 20 | # Remove -Wno-deprecated once nidas has throw's removed. 21 | env['CXXFLAGS'] = [ '-Wall','-g', '-std=c++11' ] 22 | 23 | sources = Split(""" 24 | main.cc 25 | CalibrationWizard.cc 26 | TestA2DPage.cc 27 | AutoCalClient.cc 28 | TreeItem.cc 29 | TreeModel.cc 30 | Calibrator.cc 31 | """) 32 | 33 | auto_cal = env.NidasProgram('auto_cal', sources) 34 | 35 | name = env.subst("${TARGET.filebase}", target=auto_cal) 36 | 37 | inode = env.Install('$PREFIX/bin', auto_cal) 38 | env.Clean('install', inode) 39 | -------------------------------------------------------------------------------- /PolyEval.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2012, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | #ifndef _numeric_PolyEval_h_ 27 | #define _numeric_PolyEval_h_ 28 | 29 | #include 30 | 31 | namespace numeric 32 | { 33 | 34 | inline double PolyEval(double *cof, unsigned int order, double target) 35 | { 36 | if (order == 0) 37 | return 0.0; 38 | 39 | int corder = order - 1; 40 | 41 | double out = cof[corder]; 42 | 43 | for (unsigned int k = 1; k < order; k++) 44 | out = cof[corder-k] + target * out; 45 | 46 | return out; 47 | } 48 | 49 | inline double PolyEval(std::vector cof, double target) 50 | { 51 | return PolyEval(&cof[0], cof.size(), target); 52 | } 53 | 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /Calibrator.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | #ifndef CALIBRATOR_H 27 | #define CALIBRATOR_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | //#include 38 | #include 39 | #include 40 | 41 | #include "AutoCalClient.h" 42 | 43 | using namespace nidas::core; 44 | using namespace nidas::dynld; 45 | using namespace std; 46 | 47 | namespace n_u = nidas::util; 48 | 49 | /** 50 | * @class Calibrator 51 | * Thread to collect data from dsm_server via AutoCalClient for 52 | * both auto cal and diagnostic modes. 53 | */ 54 | class Calibrator : public QThread 55 | { 56 | Q_OBJECT 57 | 58 | public: 59 | Calibrator( AutoCalClient *acc ); 60 | 61 | ~Calibrator(); 62 | 63 | inline void setTestVoltage() { _testVoltage = true; }; 64 | 65 | bool setup(QString host, QString mode); 66 | 67 | void run(); 68 | 69 | signals: 70 | void setValue(int progress); 71 | 72 | public slots: 73 | void cancel(); 74 | 75 | private: 76 | bool _testVoltage; 77 | 78 | bool _canceled; 79 | 80 | AutoCalClient* _acc; 81 | 82 | RawSampleInputStream* _sis; 83 | 84 | SamplePipeline* _pipeline; 85 | 86 | mapdsmLocations; 87 | }; 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "Calibrator.h" 32 | #include "CalibrationWizard.h" 33 | 34 | //#include "logx/Logging.h" 35 | 36 | void usage() 37 | { 38 | cerr << "Usage: auto_cal [options]\n"; 39 | cerr << " --help,-h This usage info.\n\n"; 40 | //logx::LogUsage(cerr); 41 | } 42 | 43 | /* --------------------------------------------------------------------- */ 44 | 45 | int main(int argc, char *argv[]) 46 | { 47 | // logx::ParseLogArgs (argc, argv, true/*skip usage*/); 48 | 49 | // Create the application so qt can extract its options. 50 | QApplication app(argc, argv); 51 | 52 | // Parse arguments list 53 | std::vector args(argv+1, argv+argc); 54 | unsigned int i = 0; 55 | while (i < args.size()) 56 | { 57 | if (args[i] == "--help" || args[i] == "-h") 58 | { 59 | usage(); 60 | ::exit(0); 61 | } 62 | } 63 | 64 | // Install international language translator 65 | QString translatorFileName = QLatin1String("qt_"); 66 | translatorFileName += QLocale::system().name(); 67 | QTranslator *translator = new QTranslator(&app); 68 | if (translator->load(translatorFileName, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) 69 | app.installTranslator(translator); 70 | 71 | AutoCalClient acc; 72 | 73 | Calibrator calibrator(&acc); 74 | 75 | CalibrationWizard wizard(&calibrator, &acc); 76 | 77 | wizard.show(); 78 | 79 | return app.exec(); 80 | } 81 | -------------------------------------------------------------------------------- /TestA2DPage.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | // design taken from 'examples/dialogs/licensewizard' 27 | 28 | #ifndef _TestA2DPage_h_ 29 | #define _TestA2DPage_h_ 30 | 31 | #include 32 | #include 33 | 34 | #include "Calibrator.h" 35 | #include "TreeModel.h" 36 | 37 | #include 38 | #include 39 | 40 | namespace n_u = nidas::util; 41 | 42 | using namespace nidas::core; 43 | using namespace std; 44 | 45 | QT_BEGIN_NAMESPACE 46 | class QButtonGroup; 47 | class QCheckBox; 48 | class QGridLayout; 49 | class QGroupBox; 50 | class QLabel; 51 | class QPushButton; 52 | class QRadioButton; 53 | class QTreeView; 54 | class QHBoxLayout; 55 | class QVBoxLayout; 56 | class QWidget; 57 | QT_END_NAMESPACE 58 | 59 | 60 | class TestA2DPage : public QWizardPage 61 | { 62 | Q_OBJECT 63 | 64 | public: 65 | TestA2DPage(Calibrator *calibrator, AutoCalClient *acc, QWidget *parent = 0); 66 | ~TestA2DPage(); 67 | 68 | void initializePage(); 69 | int nextId() const { return -1; } 70 | 71 | signals: 72 | void TestVoltage(int channel, int level); 73 | 74 | public slots: 75 | void dispVolts(); 76 | void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); 77 | void TestVoltage(); 78 | void updateSelection(); 79 | 80 | private: 81 | int dsmId; 82 | int devId; 83 | 84 | Calibrator *calibrator; 85 | 86 | AutoCalClient *acc; 87 | 88 | void createTree(); 89 | void createGrid(); 90 | 91 | QTreeView *treeView; 92 | TreeModel *treeModel; 93 | QGroupBox *gridGroupBox; 94 | 95 | QVBoxLayout *treeLayout; 96 | QButtonGroup *buttonGroup; 97 | QHBoxLayout *mainLayout; 98 | 99 | enum { numA2DChannels = 16 }; 100 | 101 | QLabel *ChannelTitle; 102 | QLabel *VarNameTitle; 103 | QLabel *RawVoltTitle; 104 | QLabel *MesVoltTitle; 105 | QLabel *SetVoltTitle; 106 | 107 | QLabel *Channel[numA2DChannels]; 108 | QLabel *VarName[numA2DChannels]; 109 | QLabel *RawVolt[numA2DChannels]; 110 | QLabel *MesVolt[numA2DChannels]; 111 | QHBoxLayout *SetVolt[numA2DChannels]; 112 | 113 | map > vLvlBtn; 114 | 115 | QButtonGroup* vLevels[numA2DChannels]; 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /TreeItem.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | /**************************************************************************** 27 | ** 28 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 29 | ** Contact: Nokia Corporation (qt-info@nokia.com) 30 | ** 31 | ** This file is part of the examples of the Qt Toolkit. 32 | ** 33 | ** $QT_BEGIN_LICENSE:LGPL$ 34 | ** No Commercial Usage 35 | ** This file contains pre-release code and may not be distributed. 36 | ** You may use this file in accordance with the terms and conditions 37 | ** contained in the either Technology Preview License Agreement or the 38 | ** Beta Release License Agreement. 39 | ** 40 | ** GNU Lesser General Public License Usage 41 | ** Alternatively, this file may be used under the terms of the GNU Lesser 42 | ** General Public License version 2.1 as published by the Free Software 43 | ** Foundation and appearing in the file LICENSE.LGPL included in the 44 | ** packaging of this file. Please review the following information to 45 | ** ensure the GNU Lesser General Public License version 2.1 requirements 46 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 47 | ** 48 | ** In addition, as a special exception, Nokia gives you certain 49 | ** additional rights. These rights are described in the Nokia Qt LGPL 50 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 51 | ** package. 52 | ** 53 | ** GNU General Public License Usage 54 | ** Alternatively, this file may be used under the terms of the GNU 55 | ** General Public License version 3.0 as published by the Free Software 56 | ** Foundation and appearing in the file LICENSE.GPL included in the 57 | ** packaging of this file. Please review the following information to 58 | ** ensure the GNU General Public License version 3.0 requirements will be 59 | ** met: http://www.gnu.org/copyleft/gpl.html. 60 | ** 61 | ** If you are unsure which license is appropriate for your use, please 62 | ** contact the sales department at http://www.qtsoftware.com/contact. 63 | ** $QT_END_LICENSE$ 64 | ** 65 | ****************************************************************************/ 66 | 67 | #ifndef TREEITEM_H 68 | #define TREEITEM_H 69 | 70 | #include 71 | #include 72 | 73 | class TreeItem 74 | { 75 | public: 76 | TreeItem(const QList &data, TreeItem *parent = 0); 77 | ~TreeItem(); 78 | 79 | void appendChild(TreeItem *child); 80 | 81 | TreeItem *child(int row); 82 | int childCount() const; 83 | int columnCount() const; 84 | QVariant data(int column) const; 85 | int row() const; 86 | TreeItem *parent(); 87 | 88 | private: 89 | QList childItems; 90 | QList itemData; 91 | TreeItem *parentItem; 92 | }; 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /TreeModel.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | /**************************************************************************** 27 | ** 28 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 29 | ** Contact: Nokia Corporation (qt-info@nokia.com) 30 | ** 31 | ** This file is part of the examples of the Qt Toolkit. 32 | ** 33 | ** $QT_BEGIN_LICENSE:LGPL$ 34 | ** No Commercial Usage 35 | ** This file contains pre-release code and may not be distributed. 36 | ** You may use this file in accordance with the terms and conditions 37 | ** contained in the either Technology Preview License Agreement or the 38 | ** Beta Release License Agreement. 39 | ** 40 | ** GNU Lesser General Public License Usage 41 | ** Alternatively, this file may be used under the terms of the GNU Lesser 42 | ** General Public License version 2.1 as published by the Free Software 43 | ** Foundation and appearing in the file LICENSE.LGPL included in the 44 | ** packaging of this file. Please review the following information to 45 | ** ensure the GNU Lesser General Public License version 2.1 requirements 46 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 47 | ** 48 | ** In addition, as a special exception, Nokia gives you certain 49 | ** additional rights. These rights are described in the Nokia Qt LGPL 50 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 51 | ** package. 52 | ** 53 | ** GNU General Public License Usage 54 | ** Alternatively, this file may be used under the terms of the GNU 55 | ** General Public License version 3.0 as published by the Free Software 56 | ** Foundation and appearing in the file LICENSE.GPL included in the 57 | ** packaging of this file. Please review the following information to 58 | ** ensure the GNU General Public License version 3.0 requirements will be 59 | ** met: http://www.gnu.org/copyleft/gpl.html. 60 | ** 61 | ** If you are unsure which license is appropriate for your use, please 62 | ** contact the sales department at http://www.qtsoftware.com/contact. 63 | ** $QT_END_LICENSE$ 64 | ** 65 | ****************************************************************************/ 66 | 67 | #ifndef TREEMODEL_H 68 | #define TREEMODEL_H 69 | 70 | #include 71 | #include 72 | #include 73 | 74 | class TreeItem; 75 | 76 | class TreeModel : public QAbstractItemModel 77 | { 78 | Q_OBJECT 79 | 80 | public: 81 | TreeModel(const QString &data, QObject *parent = 0); 82 | ~TreeModel(); 83 | 84 | QVariant data(const QModelIndex &index, int role) const; 85 | Qt::ItemFlags flags(const QModelIndex &index) const; 86 | QVariant headerData(int section, Qt::Orientation orientation, 87 | int role = Qt::DisplayRole) const; 88 | QModelIndex index(int row, int column, 89 | const QModelIndex &parent = QModelIndex()) const; 90 | QModelIndex parent(const QModelIndex &index) const; 91 | int rowCount(const QModelIndex &parent = QModelIndex()) const; 92 | int columnCount(const QModelIndex &parent = QModelIndex()) const; 93 | 94 | private: 95 | void setupModelData(const QStringList &lines, TreeItem *parent); 96 | 97 | TreeItem *rootItem; 98 | }; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /TreeItem.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | /**************************************************************************** 27 | ** 28 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 29 | ** Contact: Nokia Corporation (qt-info@nokia.com) 30 | ** 31 | ** This file is part of the examples of the Qt Toolkit. 32 | ** 33 | ** $QT_BEGIN_LICENSE:LGPL$ 34 | ** No Commercial Usage 35 | ** This file contains pre-release code and may not be distributed. 36 | ** You may use this file in accordance with the terms and conditions 37 | ** contained in the either Technology Preview License Agreement or the 38 | ** Beta Release License Agreement. 39 | ** 40 | ** GNU Lesser General Public License Usage 41 | ** Alternatively, this file may be used under the terms of the GNU Lesser 42 | ** General Public License version 2.1 as published by the Free Software 43 | ** Foundation and appearing in the file LICENSE.LGPL included in the 44 | ** packaging of this file. Please review the following information to 45 | ** ensure the GNU Lesser General Public License version 2.1 requirements 46 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 47 | ** 48 | ** In addition, as a special exception, Nokia gives you certain 49 | ** additional rights. These rights are described in the Nokia Qt LGPL 50 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 51 | ** package. 52 | ** 53 | ** GNU General Public License Usage 54 | ** Alternatively, this file may be used under the terms of the GNU 55 | ** General Public License version 3.0 as published by the Free Software 56 | ** Foundation and appearing in the file LICENSE.GPL included in the 57 | ** packaging of this file. Please review the following information to 58 | ** ensure the GNU General Public License version 3.0 requirements will be 59 | ** met: http://www.gnu.org/copyleft/gpl.html. 60 | ** 61 | ** If you are unsure which license is appropriate for your use, please 62 | ** contact the sales department at http://www.qtsoftware.com/contact. 63 | ** $QT_END_LICENSE$ 64 | ** 65 | ****************************************************************************/ 66 | 67 | /* 68 | treeitem.cpp 69 | 70 | A container for items of data supplied by the simple tree model. 71 | */ 72 | 73 | #include 74 | 75 | #include "TreeItem.h" 76 | 77 | TreeItem::TreeItem(const QList &data, TreeItem *parent) 78 | { 79 | parentItem = parent; 80 | itemData = data; 81 | } 82 | 83 | TreeItem::~TreeItem() 84 | { 85 | qDeleteAll(childItems); 86 | } 87 | 88 | void TreeItem::appendChild(TreeItem *item) 89 | { 90 | childItems.append(item); 91 | } 92 | 93 | TreeItem *TreeItem::child(int row) 94 | { 95 | return childItems.value(row); 96 | } 97 | 98 | int TreeItem::childCount() const 99 | { 100 | return childItems.count(); 101 | } 102 | 103 | int TreeItem::columnCount() const 104 | { 105 | return itemData.count(); 106 | } 107 | 108 | QVariant TreeItem::data(int column) const 109 | { 110 | return itemData.value(column); 111 | } 112 | 113 | TreeItem *TreeItem::parent() 114 | { 115 | return parentItem; 116 | } 117 | 118 | int TreeItem::row() const 119 | { 120 | if (parentItem) 121 | return parentItem->childItems.indexOf(const_cast(this)); 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /CalibrationWizard.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | // design taken from 'examples/dialogs/licensewizard' 27 | 28 | #ifndef CalibrationWizard_H 29 | #define CalibrationWizard_H 30 | 31 | #include 32 | 33 | #include "Calibrator.h" 34 | #include "TreeModel.h" 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | 41 | namespace n_u = nidas::util; 42 | 43 | using namespace nidas::core; 44 | using namespace std; 45 | 46 | QT_BEGIN_NAMESPACE 47 | class QButtonGroup; 48 | class QGridLayout; 49 | class QGroupBox; 50 | class QLabel; 51 | class QRadioButton; 52 | class QProgressDialog; 53 | class QSocketNotifier; 54 | class QTreeView; 55 | class QHBoxLayout; 56 | class QVBoxLayout; 57 | class QWidget; 58 | QT_END_NAMESPACE 59 | 60 | class SetupPage; 61 | class TestA2DPage; 62 | class AutoCalPage; 63 | 64 | /** 65 | * GUI logic for auto calibration tool. Creates either AutoCalPage or TestA2DPage 66 | * based on user input. 67 | */ 68 | class CalibrationWizard : public QWizard 69 | { 70 | Q_OBJECT 71 | 72 | public: 73 | CalibrationWizard(Calibrator *calibrator, AutoCalClient *acc, QWidget *parent = 0); 74 | 75 | ~CalibrationWizard(); 76 | 77 | enum { Page_Setup, Page_AutoCal, Page_TestA2D }; 78 | 79 | public slots: 80 | // Qt signal handler. 81 | void handleSignal(); 82 | 83 | signals: 84 | void dialogClosed(); 85 | 86 | protected: 87 | void accept(); 88 | 89 | void closeEvent(QCloseEvent *event); 90 | 91 | private: 92 | AutoCalClient *acc; 93 | 94 | Calibrator *calibrator; 95 | 96 | // Unix signal handler. 97 | static void sigAction(int sig, siginfo_t* siginfo, void* vptr) 98 | { _instance->cleanup(sig, siginfo, vptr); } 99 | 100 | void cleanup(int sig, siginfo_t* siginfo, void* vptr); 101 | 102 | static CalibrationWizard *_instance; 103 | 104 | static int signalFd[2]; 105 | 106 | SetupPage* _SetupPage; 107 | TestA2DPage* _TestA2DPage; 108 | AutoCalPage* _AutoCalPage; 109 | 110 | QSocketNotifier *_snSignal; 111 | }; 112 | 113 | 114 | class SetupPage : public QWizardPage 115 | { 116 | Q_OBJECT 117 | 118 | public: 119 | SetupPage(Calibrator *calibrator, QWidget *parent = 0); 120 | 121 | int nextId() const; 122 | 123 | private: 124 | Calibrator *calibrator; 125 | 126 | QLabel *topLabel; 127 | QRadioButton *testa2dRadioButton; 128 | QRadioButton *autocalRadioButton; 129 | }; 130 | 131 | 132 | class AutoCalPage : public QWizardPage 133 | { 134 | Q_OBJECT 135 | 136 | public: 137 | AutoCalPage(Calibrator *calibrator, AutoCalClient *acc, QWidget *parent = 0); 138 | 139 | void initializePage(); 140 | int nextId() const { return -1; } 141 | void setVisible(bool visible); 142 | 143 | QProgressDialog* qPD; 144 | 145 | public slots: 146 | void errMessage(const QString& message); 147 | void setValue(int progress); 148 | void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); 149 | 150 | private slots: 151 | void saveButtonClicked(); 152 | 153 | private: 154 | int dsmId; 155 | int devId; 156 | 157 | Calibrator *calibrator; 158 | 159 | AutoCalClient *acc; 160 | 161 | void createTree(); 162 | void createGrid(); 163 | 164 | QTreeView *treeView; 165 | TreeModel *treeModel; 166 | QGroupBox *gridGroupBox; 167 | 168 | QVBoxLayout *treeLayout; 169 | QButtonGroup *buttonGroup; 170 | QHBoxLayout *mainLayout; 171 | 172 | enum { numA2DChannels = 8 }; // We only auto_cal ncar_a2d cards. 173 | 174 | QLabel *ChannelTitle; 175 | QLabel *VarNameTitle; 176 | QLabel *TimeStampTitle; 177 | QLabel *TemperatureTitle; 178 | QLabel *IntcpTitle; 179 | QLabel *SlopeTitle; 180 | 181 | QLabel *Channel[numA2DChannels]; 182 | QLabel *VarName[numA2DChannels]; 183 | 184 | QLabel *OldTimeStamp[numA2DChannels]; 185 | QLabel *OldTemperature[numA2DChannels]; 186 | QLabel *OldIntcp[numA2DChannels]; 187 | QLabel *OldSlope[numA2DChannels]; 188 | 189 | QLabel *NewTimeStamp[numA2DChannels]; 190 | QLabel *NewTemperature[numA2DChannels]; 191 | QLabel *NewIntcp[numA2DChannels]; 192 | QLabel *NewSlope[numA2DChannels]; 193 | }; 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /TreeModel.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | /**************************************************************************** 27 | ** 28 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 29 | ** Contact: Nokia Corporation (qt-info@nokia.com) 30 | ** 31 | ** This file is part of the examples of the Qt Toolkit. 32 | ** 33 | ** $QT_BEGIN_LICENSE:LGPL$ 34 | ** No Commercial Usage 35 | ** This file contains pre-release code and may not be distributed. 36 | ** You may use this file in accordance with the terms and conditions 37 | ** contained in the either Technology Preview License Agreement or the 38 | ** Beta Release License Agreement. 39 | ** 40 | ** GNU Lesser General Public License Usage 41 | ** Alternatively, this file may be used under the terms of the GNU Lesser 42 | ** General Public License version 2.1 as published by the Free Software 43 | ** Foundation and appearing in the file LICENSE.LGPL included in the 44 | ** packaging of this file. Please review the following information to 45 | ** ensure the GNU Lesser General Public License version 2.1 requirements 46 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 47 | ** 48 | ** In addition, as a special exception, Nokia gives you certain 49 | ** additional rights. These rights are described in the Nokia Qt LGPL 50 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 51 | ** package. 52 | ** 53 | ** GNU General Public License Usage 54 | ** Alternatively, this file may be used under the terms of the GNU 55 | ** General Public License version 3.0 as published by the Free Software 56 | ** Foundation and appearing in the file LICENSE.GPL included in the 57 | ** packaging of this file. Please review the following information to 58 | ** ensure the GNU General Public License version 3.0 requirements will be 59 | ** met: http://www.gnu.org/copyleft/gpl.html. 60 | ** 61 | ** If you are unsure which license is appropriate for your use, please 62 | ** contact the sales department at http://www.qtsoftware.com/contact. 63 | ** $QT_END_LICENSE$ 64 | ** 65 | ****************************************************************************/ 66 | 67 | /* 68 | treemodel.cpp 69 | 70 | Provides a simple tree model to show how to create and use hierarchical 71 | models. 72 | */ 73 | 74 | #include 75 | 76 | #include "TreeItem.h" 77 | #include "TreeModel.h" 78 | 79 | TreeModel::TreeModel(const QString &data, QObject *parent) 80 | : QAbstractItemModel(parent) 81 | { 82 | QList rootData; 83 | rootData << "Location" << "Device" << "Id"; 84 | rootItem = new TreeItem(rootData); 85 | setupModelData(data.split(QString("\n")), rootItem); 86 | } 87 | 88 | TreeModel::~TreeModel() 89 | { 90 | delete rootItem; 91 | } 92 | 93 | int TreeModel::columnCount(const QModelIndex &parent) const 94 | { 95 | if (parent.isValid()) 96 | return static_cast(parent.internalPointer())->columnCount(); 97 | else 98 | return rootItem->columnCount(); 99 | } 100 | 101 | QVariant TreeModel::data(const QModelIndex &index, int role) const 102 | { 103 | if (!index.isValid()) 104 | return QVariant(); 105 | 106 | if (role != Qt::DisplayRole) 107 | return QVariant(); 108 | 109 | TreeItem *item = static_cast(index.internalPointer()); 110 | 111 | return item->data(index.column()); 112 | } 113 | 114 | Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const 115 | { 116 | if (!index.isValid()) 117 | return Qt::NoItemFlags; 118 | 119 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 120 | } 121 | 122 | QVariant TreeModel::headerData(int section, Qt::Orientation orientation, 123 | int role) const 124 | { 125 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) 126 | return rootItem->data(section); 127 | 128 | return QVariant(); 129 | } 130 | 131 | QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) 132 | const 133 | { 134 | if (!hasIndex(row, column, parent)) 135 | return QModelIndex(); 136 | 137 | TreeItem *parentItem; 138 | 139 | if (!parent.isValid()) 140 | parentItem = rootItem; 141 | else 142 | parentItem = static_cast(parent.internalPointer()); 143 | 144 | TreeItem *childItem = parentItem->child(row); 145 | if (childItem) 146 | return createIndex(row, column, childItem); 147 | else 148 | return QModelIndex(); 149 | } 150 | 151 | QModelIndex TreeModel::parent(const QModelIndex &index) const 152 | { 153 | if (!index.isValid()) 154 | return QModelIndex(); 155 | 156 | TreeItem *childItem = static_cast(index.internalPointer()); 157 | TreeItem *parentItem = childItem->parent(); 158 | 159 | if (parentItem == rootItem) 160 | return QModelIndex(); 161 | 162 | return createIndex(parentItem->row(), 0, parentItem); 163 | } 164 | 165 | int TreeModel::rowCount(const QModelIndex &parent) const 166 | { 167 | TreeItem *parentItem; 168 | if (parent.column() > 0) 169 | return 0; 170 | 171 | if (!parent.isValid()) 172 | parentItem = rootItem; 173 | else 174 | parentItem = static_cast(parent.internalPointer()); 175 | 176 | return parentItem->childCount(); 177 | } 178 | 179 | void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) 180 | { 181 | QList parents; 182 | QList indentations; 183 | parents << parent; 184 | indentations << 0; 185 | 186 | int number = 0; 187 | 188 | while (number < lines.count()) { 189 | int position = 0; 190 | while (position < lines[number].length()) { 191 | if (lines[number].mid(position, 1) != " ") 192 | break; 193 | position++; 194 | } 195 | 196 | QString lineData = lines[number].mid(position).trimmed(); 197 | 198 | if (!lineData.isEmpty()) { 199 | // Read the column data from the rest of the line. 200 | #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 201 | QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts); 202 | #else 203 | QStringList columnStrings = lineData.split("\t", Qt::SkipEmptyParts); 204 | #endif 205 | QList columnData; 206 | for (int column = 0; column < columnStrings.count(); ++column) 207 | columnData << columnStrings[column]; 208 | 209 | if (position > indentations.last()) { 210 | // The last child of the current parent is now the new parent 211 | // unless the current parent has no children. 212 | 213 | if (parents.last()->childCount() > 0) { 214 | parents << parents.last()->child(parents.last()->childCount()-1); 215 | indentations << position; 216 | } 217 | } else { 218 | while (position < indentations.last() && parents.count() > 0) { 219 | parents.pop_back(); 220 | indentations.pop_back(); 221 | } 222 | } 223 | 224 | // Append a new item to the current parent's list of children. 225 | parents.last()->appendChild(new TreeItem(columnData, parents.last())); 226 | } 227 | 228 | number++; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /AutoCalClient.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | #ifndef AUTOCALCLIENT_H 27 | #define AUTOCALCLIENT_H 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #define MAX_A2D_CHANNELS 32 // Number of A/D's per card 40 | #define NSAMPS 100 41 | //#define SIMULATE 42 | 43 | using namespace nidas::core; 44 | using namespace std; 45 | 46 | enum stateEnum { GATHER, DONE, DEAD }; 47 | 48 | enum fillState { SKIP, PEND, EMPTY, FULL }; 49 | 50 | // Card setup as returned by the dsm/card. 51 | struct a2d_setup 52 | { 53 | int gain[MAX_A2D_CHANNELS]; // gain settings 54 | // In old system, was 0,5,10. We switched to bool:biplor. 55 | // SensorClasses are returning 0 for bipolar=true and 1 for bipolar=false. 56 | int offset[MAX_A2D_CHANNELS]; // Offset flags 57 | int calset[MAX_A2D_CHANNELS]; // cal voltage channels 58 | int vcal; // cal voltage 59 | }; 60 | 61 | /** 62 | * @class AutoCalClient 63 | * Hodge podge class. Generates QtTree of DSMs for the Wizard, collects 64 | * data from dsm_server, progress bar, stores and displays results from all 65 | * analog cards. 66 | */ 67 | class AutoCalClient: public QObject, public SampleClient 68 | { 69 | Q_OBJECT 70 | 71 | public: 72 | 73 | AutoCalClient(); 74 | 75 | /// Implementation of SampleClient::flush(). 76 | void flush() throw() {} 77 | 78 | /// Reassemble a nidas dsmId and sensorId back into id. 79 | dsm_sample_id_t id(unsigned int dsmId, unsigned int devId); 80 | 81 | void setTestVoltage(int dsmId, int devId); 82 | 83 | a2d_setup GetA2dSetup(int dsmId, int devId); 84 | 85 | bool Setup(DSMSensor* sensor); 86 | 87 | void createQtTreeModel( mapdsmLocations ); 88 | 89 | enum stateEnum SetNextCalVoltage(enum stateEnum state); 90 | 91 | bool receive(const Sample* samp) throw(); 92 | 93 | bool Gathered(); 94 | 95 | void DisplayResults(); 96 | 97 | int maxProgress() { return nLevels * NSAMPS + 1; }; 98 | 99 | string GetTreeModel() { return QTreeModel.str(); }; 100 | 101 | // Save all cards at once. 102 | void SaveAllCalFiles(); 103 | 104 | // Save an individual analog card. 105 | void SaveCalFile(uint dsmId, uint devId); 106 | 107 | list GetVoltageLevels(); 108 | 109 | list GetVoltageLevels(uint dsmId, uint devId, uint chn); 110 | 111 | string GetVarName(uint dsmId, uint devId, uint chn); 112 | 113 | /** 114 | * For sensor classes that return a Vdc, do nothing, just return the test 115 | * data. For gpDAQ which returns the raw counts, scale it to uncalibrated 116 | * voltage. 117 | */ 118 | float GetVoltageData(uint dsmId, uint devId, uint chn); 119 | 120 | string GetOldTimeStamp(uint dsmId, uint devId, uint chn); 121 | string GetNewTimeStamp(uint dsmId, uint devId, uint chn); 122 | 123 | float GetOldTemperature(uint dsmId, uint devId, uint chn); 124 | float GetNewTemperature(uint dsmId, uint devId, uint chn); 125 | 126 | vector GetOldCals(uint dsmId, uint devId, uint chn); 127 | vector GetNewCals(uint dsmId, uint devId, uint chn); 128 | 129 | unsigned int nLevels; 130 | 131 | int progress; 132 | 133 | typedef map channel_a_type; // indexed by chn 134 | typedef map device_a_type; // indexed by devId 135 | typedef map dsm_a_type; // indexed by dsmId 136 | typedef map< int, dsm_a_type> level_a_type; // indexed by level 137 | 138 | /// calActv[level][dsmId][devId][chn] 139 | level_a_type calActv; 140 | 141 | /// testData[dsmId][devId][chn] 142 | map > > testData; 143 | 144 | signals: 145 | void dispVolts(); 146 | void errMessage(const QString& message); 147 | void updateSelection(); 148 | 149 | public slots: 150 | void TestVoltage(int channel, int level); 151 | 152 | private: 153 | bool readCalFile(DSMSensor* sensor, string card); 154 | 155 | string ChnSetDesc(unsigned int val); 156 | 157 | bool testVoltage; 158 | int tvDsmId; 159 | int tvDevId; 160 | 161 | ostringstream QTreeModel; 162 | ostringstream QStrBuf; 163 | 164 | dsm_time_t lastTimeStamp; 165 | 166 | /// voltageLevels["GB"] indexed by "1T", "2F", "2T", or "4F" 167 | map > voltageLevels; 168 | 169 | struct sA2dSampleInfo { 170 | uint dsmId; 171 | uint devId; 172 | uint rate; 173 | bool isaTemperatureId; 174 | map channel; // indexed by varId 175 | }; 176 | map sampleInfo; // indexed by sampId 177 | 178 | map > temperatureId; 179 | 180 | map dsmNames; // indexed by dsmId 181 | map devNames; // indexed by devId 182 | map cardType; // indexed by devId 183 | map devNchannels; // indexed by devId 184 | map< int, uint> slowestRate; // indexed by level 185 | 186 | typedef vector data_d_type; 187 | 188 | typedef map< int, data_d_type> level_d_type; // indexed by level 189 | typedef map channel_d_type; // indexed by chn 190 | typedef map device_d_type; // indexed by devId 191 | typedef map dsm_d_type; // indexed by dsmId 192 | 193 | /// calData[dsmId][devId][chn][level] 194 | dsm_d_type calData; 195 | 196 | /// index to active voltage level 197 | int idxVltLvl; 198 | 199 | /// active voltage level 200 | int VltLvl; 201 | 202 | /// isNAN[dsmId][devId][chn][level] 203 | map > > > isNAN; 204 | 205 | /// calFilePath[dsmId][devId] 206 | map > calFilePath; 207 | 208 | /// calFileName[dsmId][devId] 209 | map > calFileName; 210 | 211 | /// calFileSaved[dsmId][devId] 212 | map > calFileSaved; 213 | 214 | /// calFileResults[dsmId][devId] 215 | map > calFileResults; 216 | 217 | /// resultTemperature[dsmId][devId] 218 | map > resultTemperature; 219 | 220 | /// temperatureData[dsmId][devId] 221 | map > temperatureData; 222 | 223 | /// VarNames[dsmId][devId][chn] 224 | map > > VarNames; 225 | 226 | /// Gains[dsmId][devId][chn] 227 | map > > Gains; 228 | 229 | /// Bplrs[dsmId][devId][chn] 230 | map > > Bplrs; 231 | 232 | /// timeStamp[dsmId][devId][chn] 233 | map > > timeStamp; 234 | 235 | /// calFileTime[dsmId][devId][gain][bplr] 236 | map > > > calFileTime; 237 | 238 | /// calFileIntcp[dsmId][devId][chn][gain][bplr] 239 | map > > > > > calFileCals; 240 | 241 | /// resultIntcp[dsmId][devId][chn][gain][bplr] 242 | map > > > > > resultCals; 243 | 244 | level_a_type::iterator iLevel; 245 | dsm_a_type::iterator iDsm; 246 | device_a_type::iterator iDevice; 247 | channel_a_type::iterator iChannel; 248 | }; 249 | 250 | #endif 251 | -------------------------------------------------------------------------------- /Calibrator.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | #include "Calibrator.h" 27 | #include "AutoCalClient.h" 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | #ifdef SIMULATE 44 | #include 45 | #endif 46 | 47 | #include 48 | 49 | using namespace nidas::core; 50 | using namespace nidas::dynld; 51 | using namespace nidas::dynld::raf; 52 | using namespace std; 53 | 54 | namespace n_u = nidas::util; 55 | 56 | string stateEnumDesc[] = {"GATHER", "DONE", "DEAD" }; 57 | 58 | class AutoProject 59 | { 60 | public: 61 | AutoProject() { Project::getInstance(); } 62 | ~AutoProject() { Project::destroyInstance(); } 63 | }; 64 | 65 | Calibrator::Calibrator( AutoCalClient *acc ): 66 | _testVoltage(false), 67 | _canceled(false), 68 | _acc(acc), 69 | _sis(0), 70 | _pipeline(0) 71 | { 72 | AutoProject project; 73 | } 74 | 75 | 76 | Calibrator::~Calibrator() 77 | { 78 | cout << "Calibrator::~Calibrator" << endl; 79 | 80 | if (_pipeline) 81 | _pipeline->getProcessedSampleSource()->removeSampleClient(_acc); 82 | 83 | if (isRunning()) { 84 | cancel(); 85 | wait(); 86 | } 87 | delete _sis; 88 | delete _pipeline; 89 | }; 90 | 91 | 92 | bool Calibrator::setup(QString host, QString mode) 93 | { 94 | cout << "Calibrator::setup(), mode=[" << mode.toStdString() << "]\n"; 95 | 96 | try { 97 | IOChannel* iochan = 0; 98 | 99 | #ifdef SIMULATE 100 | // local data file is a copy of: 101 | // /scr/raf/Raw_Data/PLOWS/20091106_161332_ff04.ads 102 | list dataFileNames; 103 | dataFileNames.push_back("/home/data/Raw_Data/20210529_132233_rf01.ads"); 104 | nidas::core::FileSet* fset = 105 | nidas::core::FileSet::getFileSet(dataFileNames); 106 | iochan = fset->connect(); 107 | cout << "SIMULATE! using " << dataFileNames.front() << endl; 108 | #else 109 | // real time operation 110 | cout << "hostName: " << host.toStdString() << endl; 111 | n_u::Socket * sock = new n_u::Socket(host.toStdString(), NIDAS_SVC_REQUEST_PORT_UDP); 112 | iochan = new nidas::core::Socket(sock); 113 | cout << "Calibrator::setup() connected to dsm_server" << endl; 114 | #endif 115 | 116 | _sis = new RawSampleInputStream(iochan); // RawSampleStream now owns the iochan ptr. 117 | _sis->setMaxSampleLength(32768); 118 | 119 | cout << "Calibrator::setup() RawSampleStream now owns the iochan ptr." << endl; 120 | 121 | // Address to use when fishing for the XML configuration. 122 | n_u::Inet4SocketAddress _configSockAddr; 123 | try { 124 | _configSockAddr = n_u::Inet4SocketAddress( 125 | n_u::Inet4Address::getByName(host.toStdString()), 126 | NIDAS_SVC_REQUEST_PORT_UDP); 127 | } 128 | catch(const n_u::UnknownHostException& e) { // shouldn't happen 129 | ostringstream ostr; 130 | ostr << "Failed to aquire XML configuration: " << e.what(); 131 | cout << ostr.str() << endl; 132 | QMessageBox::critical(0, "CANNOT start", ostr.str().c_str()); 133 | return true; 134 | } 135 | // Pull in the XML configuration from the DSM server. 136 | n_u::auto_ptr doc(requestXMLConfig(true,_configSockAddr)); 137 | 138 | Project::getInstance()->fromDOMElement(doc->getDocumentElement()); 139 | doc.release(); 140 | 141 | bool noneFound = true; 142 | 143 | _pipeline = new SamplePipeline(); 144 | cout << "_pipeline: " << _pipeline << endl; 145 | 146 | DSMConfigIterator di = Project::getInstance()->getDSMConfigIterator(); 147 | for ( ; di.hasNext(); ) { 148 | const DSMConfig* dsm = di.next(); 149 | const list& allSensors = dsm->getSensors(); 150 | 151 | list::const_iterator si; 152 | for (si = allSensors.begin(); si != allSensors.end(); ++si) { 153 | DSMSensor* sensor = *si; 154 | 155 | if (_canceled) 156 | return true; 157 | 158 | // skip non-Analog type sensors 159 | // Cal mode is for ncar_a2d only. Diag nostic mode is for all 160 | if (mode == "cal" && sensor->getClassName().compare("raf.DSMAnalogSensor")) 161 | continue; 162 | if (sensor->getClassName().compare("raf.DSMAnalogSensor") && // ncar_a2d 163 | sensor->getClassName().compare("DSC_A2DSensor") && // Diamond - ddmat 164 | sensor->getClassName().compare("raf.A2D_Serial")) // gpDAQ 165 | continue; 166 | 167 | // skip non-responsive of miss-configured sensors 168 | if ( _acc->Setup(sensor) ) 169 | continue; 170 | 171 | dsmLocations[dsm->getId()] = dsm->getLocation(); 172 | 173 | // DEBUG - print out the found calibration coeffients 174 | uint dsmId = sensor->getDSMId(); 175 | uint devId = sensor->getSensorId(); 176 | for (uint chn = 0; chn < 8; chn++) { 177 | if (_acc->GetOldCals(dsmId, devId, chn).size() > 1) { 178 | cout << __PRETTY_FUNCTION__; 179 | cout << " dsmId: " << dsmId << " devId: " << devId << " chn: " << chn; 180 | cout << " nCals: " << _acc->GetOldCals(dsmId, devId, chn).size(); 181 | cout << " Intcp: " << _acc->GetOldCals(dsmId, devId, chn)[0]; 182 | cout << " Slope: " << _acc->GetOldCals(dsmId, devId, chn)[1]; 183 | cout << endl; 184 | } 185 | } 186 | // default slopes and intersects to 1.0 and 0.0 187 | sensor->removeCalFiles(); 188 | 189 | // initialize the sensor 190 | sensor->init(); 191 | 192 | // inform the SampleInputStream of what SampleTags to expect 193 | cout << "_sis->addSampleTag(sensor->getRawSampleTag());" << endl; 194 | _sis->addSampleTag(sensor->getRawSampleTag()); 195 | 196 | // connect to the _pipeline member 197 | _pipeline->connect(sensor); 198 | 199 | noneFound = false; 200 | } 201 | } 202 | if ( noneFound ) { 203 | ostringstream ostr; 204 | ostr << "No analog cards available to calibrate!"; 205 | cout << ostr.str() << endl; 206 | QMessageBox::critical(0, "no cards", ostr.str().c_str()); 207 | return true; 208 | } 209 | cout << "Calibrator::setup() extracted analog sensors" << endl; 210 | _acc->createQtTreeModel(dsmLocations); 211 | } 212 | catch (n_u::IOException& e) { 213 | cout << "DSM server is not running!" << endl; 214 | cout << "You need to start NIDAS" << endl; 215 | return true; 216 | } 217 | cout << "Calibrator::setup() FINISHED" << endl; 218 | return false; 219 | } 220 | 221 | 222 | void Calibrator::run() 223 | { 224 | cout << "Calibrator::run()" << endl; 225 | 226 | try { 227 | _pipeline->setRealTime(true); 228 | _pipeline->setProcSorterLength(0); 229 | 230 | // 2. connect the pipeline to the SampleInputStream. 231 | _pipeline->connect(_sis); 232 | 233 | // 3. connect the client to the pipeline 234 | _pipeline->getProcessedSampleSource()->addSampleClient(_acc); 235 | 236 | try { 237 | enum stateEnum state = GATHER; 238 | while (_testVoltage) { 239 | _sis->readSamples(); // see AutoCalClient::receive 240 | if (_canceled) { 241 | cout << "Canceling diagnostics..." << endl; 242 | state = DONE; 243 | break; 244 | } 245 | } 246 | while ( (state = _acc->SetNextCalVoltage(state)) != DONE ) { 247 | 248 | cout << "state: " << stateEnumDesc[state] << endl; 249 | 250 | if (state == DONE) 251 | break; 252 | 253 | if (state == DEAD) 254 | break; 255 | 256 | cout << "gathering..." << endl; 257 | while ( _testVoltage || !_acc->Gathered() ) { 258 | 259 | if (_canceled) { 260 | cout << "Canceling calibration..." << endl; 261 | state = DONE; 262 | break; 263 | } 264 | _sis->readSamples(); // see AutoCalClient::receive 265 | 266 | // update progress bar 267 | if (!_testVoltage) 268 | emit setValue(_acc->progress); 269 | } 270 | } 271 | if (state == DONE) { 272 | _acc->DisplayResults(); 273 | 274 | // update progress bar 275 | emit setValue(_acc->progress); 276 | } 277 | } 278 | catch (n_u::EOFException& e) { 279 | cerr << e.what() << endl; 280 | } 281 | catch (n_u::IOException& e) { 282 | _pipeline->getProcessedSampleSource()->removeSampleClient(_acc); 283 | _pipeline->disconnect(_sis); 284 | _sis->close(); 285 | throw(e); 286 | } 287 | _pipeline->getProcessedSampleSource()->removeSampleClient(_acc); 288 | _pipeline->disconnect(_sis); 289 | _sis->close(); 290 | } 291 | catch (n_u::IOException& e) { 292 | cerr << e.what() << endl; 293 | } 294 | catch (n_u::Exception& e) { 295 | cerr << e.what() << endl; 296 | } 297 | cout << "Calibrator::run() FINISHED" << endl; 298 | } 299 | 300 | 301 | void Calibrator::cancel() 302 | { 303 | _canceled = true; 304 | } 305 | -------------------------------------------------------------------------------- /TestA2DPage.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | // design taken from 'examples/dialogs/licensewizard' 27 | 28 | #include "TestA2DPage.h" 29 | #include "PolyEval.h" 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | 44 | /* ---------------------------------------------------------------------------------------------- */ 45 | 46 | TestA2DPage::TestA2DPage(Calibrator *calib, AutoCalClient *acc, QWidget *parent) 47 | : QWizardPage(parent), dsmId(-1), devId(-1), calibrator(calib), acc(acc) 48 | { 49 | setTitle(tr("Test A2Ds")); 50 | setSubTitle(tr("Select a card from the tree to list channels.")); 51 | setFinalPage(true); 52 | setMinimumWidth(900); 53 | } 54 | 55 | 56 | TestA2DPage::~TestA2DPage() 57 | { 58 | cout << "TestA2DPage::~TestA2DPage()" << endl; 59 | list voltageLevels = acc->GetVoltageLevels(); 60 | list::iterator l; 61 | 62 | for (int chn = 0; chn < numA2DChannels; chn++) 63 | for ( l = voltageLevels.begin(); l != voltageLevels.end(); l++) 64 | delete vLvlBtn[*l][chn]; 65 | } 66 | 67 | 68 | void TestA2DPage::createTree() 69 | { 70 | cout << "TestA2DPage::createTree" << endl; 71 | treeView = new QTreeView(); 72 | 73 | // extract the tree from AutoCalClient. 74 | cout << acc->GetTreeModel(); 75 | treeModel = new TreeModel( QString(acc->GetTreeModel().c_str()) ); 76 | 77 | // Initialize the QTreeView 78 | treeView->setModel(treeModel); 79 | treeView->expandAll(); 80 | treeView->setMinimumWidth(300); 81 | treeView->resizeColumnToContents(0); 82 | treeView->resizeColumnToContents(1); 83 | treeView->resizeColumnToContents(2); 84 | 85 | // The dsmId(s) and devId(s) are hidden in the 3rd column. 86 | // treeView->hideColumn(2); 87 | 88 | connect(treeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), 89 | this, SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))); 90 | } 91 | 92 | 93 | void TestA2DPage::dispVolts() 94 | { 95 | if (devId == -1) return; 96 | if (dsmId == -1) return; 97 | if (dsmId == devId) return; 98 | 99 | for (int chn = 0; chn < numA2DChannels; chn++) { 100 | if ( acc->calActv[0][dsmId][devId][chn] == SKIP ) continue; 101 | 102 | // obtain current set of calibration coefficients for this channel 103 | std::vector _cals = acc->GetOldCals(dsmId, devId, chn); 104 | 105 | // apply the coefficients to the raw measured values 106 | QString raw, mes; 107 | float applied = numeric::PolyEval(_cals, acc->testData[dsmId][devId][chn]); 108 | QTextStream rstr(&raw); 109 | rstr << qSetFieldWidth(7) << qSetRealNumberPrecision(4) << acc->GetVoltageData(dsmId, devId, chn); 110 | QTextStream mstr(&mes); 111 | mstr << qSetFieldWidth(7) << qSetRealNumberPrecision(4) << applied; 112 | RawVolt[chn]->setText( raw ); 113 | MesVolt[chn]->setText( mes ); 114 | } 115 | } 116 | 117 | 118 | void TestA2DPage::updateSelection() 119 | { 120 | cout << "TestA2DPage::updateSelection" << endl; 121 | a2d_setup setup = acc->GetA2dSetup(dsmId, devId); 122 | 123 | for (int chn = 0; chn < numA2DChannels; chn++) { 124 | VarName[chn]->setText( QString( acc->GetVarName(dsmId, devId, chn).c_str() ) ); 125 | RawVolt[chn]->setText(""); 126 | MesVolt[chn]->setText(""); 127 | 128 | VarName[chn]->setHidden(false); 129 | RawVolt[chn]->setHidden(false); 130 | MesVolt[chn]->setHidden(false); 131 | 132 | list voltageLevels; 133 | list::iterator l; 134 | 135 | // hide and uncheck all voltage selection buttons for this channel 136 | voltageLevels = acc->GetVoltageLevels(); 137 | for ( l = voltageLevels.begin(); l != voltageLevels.end(); l++) { 138 | vLvlBtn[*l][chn]->setHidden(true); 139 | vLvlBtn[*l][chn]->setDown(false); 140 | // cout << "TestA2DPage::updateSelection vLvlBtn[" << *l << "][" << chn << "]->setDown(false);" << endl; 141 | } 142 | voltageLevels.clear(); 143 | 144 | // show available voltage selection buttons for this channel 145 | voltageLevels = acc->GetVoltageLevels(dsmId, devId, chn); 146 | if (!voltageLevels.empty()) { 147 | for ( l = voltageLevels.begin(); l != voltageLevels.end(); l++) 148 | vLvlBtn[*l][chn]->setHidden(false); 149 | 150 | RawVolt[chn]->setText("---"); 151 | MesVolt[chn]->setText("---"); 152 | vLvlBtn[-99][chn]->setHidden(false); 153 | 154 | // check all active channels to use the same calibration voltage 155 | if (setup.calset[chn]) 156 | vLvlBtn[setup.vcal][chn]->setDown(true); 157 | else 158 | vLvlBtn[-99][chn]->setDown(true); 159 | } 160 | } 161 | } 162 | 163 | 164 | void TestA2DPage::selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/) 165 | { 166 | cout << "TestA2DPage::selectionChanged" << endl; 167 | if (selected.indexes().count() > 0) { 168 | 169 | QModelIndex index = selected.indexes().first(); 170 | QModelIndex parent = index.parent(); 171 | 172 | QModelIndex devIdx = index.sibling(index.row(), 2); 173 | devId = treeModel->data(devIdx, Qt::DisplayRole).toInt(); 174 | 175 | QModelIndex dsmIdx = parent.sibling(parent.row(), 2); 176 | dsmId = treeModel->data(dsmIdx, Qt::DisplayRole).toInt(); 177 | 178 | if (parent == QModelIndex()) { 179 | 180 | // DSM selected not card... hide the displayed table contents. 181 | list voltageLevels; 182 | list::iterator l; 183 | voltageLevels = acc->GetVoltageLevels(); 184 | for (int chn = 0; chn < numA2DChannels; chn++) { 185 | VarName[chn]->setHidden(true); 186 | RawVolt[chn]->setHidden(true); 187 | MesVolt[chn]->setHidden(true); 188 | for ( l = voltageLevels.begin(); l != voltageLevels.end(); l++) 189 | vLvlBtn[*l][chn]->setHidden(true); 190 | } 191 | dsmId = devId; 192 | return; 193 | } 194 | acc->setTestVoltage(dsmId, devId); 195 | updateSelection(); 196 | } 197 | } 198 | 199 | 200 | void TestA2DPage::createGrid() 201 | { 202 | cout << "TestA2DPage::createGrid" << endl; 203 | gridGroupBox = new QGroupBox(tr("Set internal voltages here")); 204 | 205 | QGridLayout *layout = new QGridLayout; 206 | 207 | ChannelTitle = new QLabel( QString( "CHN" ) ); 208 | VarNameTitle = new QLabel( QString( "VARNAME" ) ); 209 | RawVoltTitle = new QLabel( QString( "RAWVOLT" ) ); 210 | MesVoltTitle = new QLabel( QString( "MESVOLT" ) ); 211 | SetVoltTitle = new QLabel( QString( "SETVOLT" ) ); 212 | 213 | QFont font; 214 | #if defined(Q_WS_X11) 215 | font.setFamily("Monospace"); 216 | #else 217 | font.setFamily("Courier New"); 218 | #endif 219 | font.setPointSize(9); 220 | setFont(font); 221 | 222 | layout->addWidget( ChannelTitle, 0, 0); 223 | layout->addWidget( VarNameTitle, 0, 1); 224 | layout->addWidget( RawVoltTitle, 0, 2); 225 | layout->addWidget( MesVoltTitle, 0, 3); 226 | layout->addWidget( SetVoltTitle, 0, 4, 1, 6); 227 | 228 | layout->setColumnMinimumWidth(0, 20); 229 | 230 | list voltageLevels = acc->GetVoltageLevels(); 231 | 232 | for (int chn = 0; chn < numA2DChannels; chn++) { 233 | Channel[chn] = new QLabel( QString("%1:").arg(chn) ); 234 | Channel[chn]->setStyleSheet("QLabel { min-width: 30px ; max-width: 30px }"); 235 | layout->addWidget(Channel[chn], chn+1, 0); 236 | 237 | VarName[chn] = new QLabel; 238 | VarName[chn]->setStyleSheet("QLabel { min-width: 100px }"); 239 | layout->addWidget(VarName[chn], chn+1, 1); 240 | 241 | RawVolt[chn] = new QLabel; 242 | RawVolt[chn]->setStyleSheet("QLabel { min-width: 60px ; max-width: 60px }"); 243 | layout->addWidget(RawVolt[chn], chn+1, 2); 244 | 245 | MesVolt[chn] = new QLabel; 246 | MesVolt[chn]->setStyleSheet("QLabel { min-width: 60px ; max-width: 60px }"); 247 | layout->addWidget(MesVolt[chn], chn+1, 3); 248 | 249 | // group buttons by channel 250 | vLevels[chn] = new QButtonGroup(); 251 | 252 | vLvlBtn[-99][chn] = new QPushButton("off"); 253 | vLvlBtn[ 0][chn] = new QPushButton("0v"); 254 | vLvlBtn[ 1][chn] = new QPushButton("1v"); 255 | vLvlBtn[ 2][chn] = new QPushButton("2.5v"); 256 | vLvlBtn[ 5][chn] = new QPushButton("5v"); 257 | vLvlBtn[ 10][chn] = new QPushButton("10v"); 258 | vLvlBtn[-10][chn] = new QPushButton("-10v"); 259 | 260 | list::iterator l; 261 | int column = 4; 262 | for ( l = voltageLevels.begin(); l != voltageLevels.end(); l++) { 263 | 264 | layout->addWidget(vLvlBtn[*l][chn], chn+1, column++); 265 | 266 | vLvlBtn[*l][chn]->setStyleSheet("QPushButton { min-width: 30px ; max-width: 30px }"); 267 | vLvlBtn[*l][chn]->setHidden(true); 268 | vLevels[chn]->addButton(vLvlBtn[*l][chn]); 269 | 270 | connect(vLvlBtn[*l][chn], SIGNAL(clicked()), 271 | this, SLOT(TestVoltage())); 272 | } 273 | } 274 | // gridGroupBox->setMinimumSize(470, 600); // width, height 275 | gridGroupBox->setStyleSheet(" QGroupBox { min-width: 470 }"); 276 | gridGroupBox->setLayout(layout); 277 | } 278 | 279 | 280 | void TestA2DPage::TestVoltage() 281 | { 282 | list voltageLevels = acc->GetVoltageLevels(); 283 | list::iterator l; 284 | 285 | for (int chn = 0; chn < numA2DChannels; chn++) 286 | for ( l = voltageLevels.begin(); l != voltageLevels.end(); l++) 287 | 288 | if((QPushButton *) sender() == vLvlBtn[*l][chn]) { 289 | acc->setTestVoltage(dsmId, devId); 290 | emit TestVoltage(chn, *l); 291 | return; 292 | } 293 | } 294 | 295 | 296 | void TestA2DPage::initializePage() 297 | { 298 | cout << "TestA2DPage::initializePage" << endl; 299 | 300 | calibrator->setTestVoltage(); 301 | acc->setTestVoltage(-1, -1); 302 | 303 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 304 | if (calibrator->setup("acserver", "diag")) return; 305 | QApplication::restoreOverrideCursor(); 306 | 307 | createTree(); 308 | createGrid(); 309 | 310 | treeView->setCurrentIndex(treeView->model()->index(0,0)); 311 | 312 | connect(this, SIGNAL(TestVoltage(int, int)), 313 | acc, SLOT(TestVoltage(int, int))); 314 | 315 | connect(acc, SIGNAL(updateSelection()), 316 | this, SLOT(updateSelection())); 317 | 318 | connect(acc, SIGNAL(dispVolts()), 319 | this, SLOT(dispVolts())); 320 | 321 | /* Doesn't exist?? --cjw Aug/2021 322 | connect(calibrator, SIGNAL(setValue(int)), 323 | this, SLOT(paint()) ); 324 | */ 325 | mainLayout = new QHBoxLayout; 326 | mainLayout->addWidget(treeView); 327 | mainLayout->addWidget(gridGroupBox); 328 | 329 | setLayout(mainLayout); 330 | 331 | calibrator->start(); // see Calibrator::run 332 | } 333 | -------------------------------------------------------------------------------- /CalibrationWizard.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | // design taken from 'examples/dialogs/licensewizard' 27 | 28 | #include "CalibrationWizard.h" 29 | #include "TestA2DPage.h" 30 | #include "Calibrator.h" 31 | #include "PolyEval.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | 48 | 49 | CalibrationWizard *CalibrationWizard::_instance; 50 | 51 | CalibrationWizard::CalibrationWizard(Calibrator *calib, AutoCalClient *acc, QWidget *parent) 52 | : QWizard(parent, Qt::Window), acc(acc), calibrator(calib) 53 | { 54 | setOption(QWizard::NoBackButtonOnStartPage, true); 55 | setOption(QWizard::NoBackButtonOnLastPage, true); 56 | setOption(QWizard::IndependentPages, true); 57 | setOption(QWizard::NoCancelButton, true); 58 | 59 | _SetupPage = new SetupPage(calib); 60 | _TestA2DPage = new TestA2DPage(calib, acc); 61 | _AutoCalPage = new AutoCalPage(calib, acc); 62 | 63 | setPage(Page_Setup, _SetupPage); 64 | setPage(Page_TestA2D, _TestA2DPage); 65 | setPage(Page_AutoCal, _AutoCalPage); 66 | 67 | setStartId(Page_Setup); 68 | 69 | #ifndef Q_WS_MAC 70 | setWizardStyle(ModernStyle); 71 | #endif 72 | // TODO 73 | // setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png")); 74 | 75 | setWindowTitle(tr("Auto Calibration Wizard")); 76 | 77 | // setup UNIX signal handler 78 | sigset_t sigset; 79 | sigemptyset(&sigset); 80 | sigaddset(&sigset,SIGHUP); 81 | sigaddset(&sigset,SIGINT); 82 | sigaddset(&sigset,SIGTERM); 83 | sigprocmask(SIG_UNBLOCK,&sigset,(sigset_t*)0); 84 | 85 | struct sigaction act; 86 | sigemptyset(&sigset); 87 | act.sa_mask = sigset; 88 | act.sa_flags = SA_SIGINFO; 89 | act.sa_sigaction = CalibrationWizard::sigAction; 90 | sigaction(SIGHUP ,&act,(struct sigaction *)0); 91 | sigaction(SIGINT ,&act,(struct sigaction *)0); 92 | sigaction(SIGTERM,&act,(struct sigaction *)0); 93 | 94 | // setup sockets to receive UNIX signals 95 | if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) 96 | qFatal("Couldn't create socketpair"); 97 | 98 | _snSignal = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); 99 | connect(_snSignal, SIGNAL(activated(int)), this, SLOT(handleSignal())); 100 | _instance = this; 101 | } 102 | 103 | CalibrationWizard::~CalibrationWizard() 104 | { 105 | delete _snSignal; 106 | delete _AutoCalPage; 107 | delete _TestA2DPage; 108 | delete _SetupPage; 109 | } 110 | 111 | 112 | // signal handler cleanup work. 113 | void CalibrationWizard::cleanup(int sig, siginfo_t* siginfo, void* vptr) 114 | { 115 | cout << 116 | "received signal " << strsignal(sig) << '(' << sig << ')' << 117 | ", si_signo=" << (siginfo ? siginfo->si_signo : -1) << 118 | ", si_errno=" << (siginfo ? siginfo->si_errno : -1) << 119 | ", si_code=" << (siginfo ? siginfo->si_code : -1) << endl; 120 | 121 | 122 | // Clear any residual auto-cal 123 | acc->SetNextCalVoltage(DONE); 124 | 125 | char a = 1; 126 | 127 | switch(sig) { 128 | case SIGHUP: 129 | case SIGTERM: 130 | case SIGINT: 131 | ::write(signalFd[0], &a, sizeof(a)); 132 | break; 133 | } 134 | } 135 | 136 | 137 | void CalibrationWizard::handleSignal() 138 | { 139 | char tmp; 140 | ::read(signalFd[1], &tmp, sizeof(tmp)); 141 | 142 | // do Qt stuff 143 | emit close(); 144 | } 145 | 146 | 147 | /* static */ 148 | int CalibrationWizard::signalFd[2] = {0, 0}; 149 | 150 | 151 | void CalibrationWizard::accept() 152 | { 153 | calibrator->cancel(); 154 | calibrator->wait(); 155 | QWizard::accept(); 156 | } 157 | 158 | 159 | void CalibrationWizard::closeEvent(QCloseEvent *event) 160 | { 161 | cout << __PRETTY_FUNCTION__ << endl; 162 | if (!calibrator) return; 163 | 164 | calibrator->cancel(); 165 | calibrator->wait(); 166 | exit(0); 167 | } 168 | 169 | 170 | /* ---------------------------------------------------------------------------------------------- */ 171 | 172 | SetupPage::SetupPage(Calibrator *calib, QWidget *parent) 173 | : QWizardPage(parent), calibrator(calib) 174 | { 175 | setTitle(tr("Setup")); 176 | 177 | // TODO 178 | // setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark.png")); 179 | 180 | topLabel = new QLabel(tr("This will search the NIDAS server and all of its " 181 | "connected DSMs for NCAR based A2D cards.\n\nAll " 182 | "cards can only operate as configured.\n\nYou can " 183 | "either test a card's channels by manually setting " 184 | "them, or you can automatically calibrate all of " 185 | "the cards:\n")); 186 | topLabel->setWordWrap(true); 187 | 188 | testa2dRadioButton = new QRadioButton(tr("&Test A2D channels")); 189 | autocalRadioButton = new QRadioButton(tr("&Auto Calibrate")); 190 | 191 | QVBoxLayout *layout = new QVBoxLayout; 192 | layout->addWidget(topLabel); 193 | layout->addWidget(testa2dRadioButton); 194 | layout->addWidget(autocalRadioButton); 195 | setLayout(layout); 196 | } 197 | 198 | 199 | int SetupPage::nextId() const 200 | { 201 | if (autocalRadioButton->isChecked()) { 202 | return CalibrationWizard::Page_AutoCal; 203 | } 204 | else if (testa2dRadioButton->isChecked()) { 205 | return CalibrationWizard::Page_TestA2D; 206 | } 207 | return CalibrationWizard::Page_Setup; 208 | } 209 | 210 | /* ---------------------------------------------------------------------------------------------- */ 211 | 212 | AutoCalPage::AutoCalPage(Calibrator *calib, AutoCalClient *acc, QWidget *parent) 213 | : QWizardPage(parent), dsmId(-1), devId(-1), calibrator(calib), acc(acc) 214 | { 215 | setTitle(tr("Auto Calibration")); 216 | setSubTitle(tr("Select a card from the tree to review the results.")); 217 | setFinalPage(true); 218 | } 219 | 220 | 221 | void AutoCalPage::setVisible(bool visible) 222 | { 223 | QWizardPage::setVisible(visible); 224 | 225 | if (visible) { 226 | wizard()->setButtonText(QWizard::CustomButton1, tr("&Save")); 227 | wizard()->setOption(QWizard::HaveCustomButton1, true); 228 | connect(wizard(), SIGNAL(customButtonClicked(int)), 229 | this, SLOT(saveButtonClicked())); 230 | } else { 231 | wizard()->setOption(QWizard::HaveCustomButton1, false); 232 | disconnect(wizard(), SIGNAL(customButtonClicked(int)), 233 | this, SLOT(saveButtonClicked())); 234 | } 235 | } 236 | 237 | 238 | void AutoCalPage::saveButtonClicked() 239 | { 240 | /* original behavior was to save the card selected on the GUI. 241 | if (dsmId == devId) { 242 | QMessageBox::information(0, "notice", "You must select a card to save!"); 243 | return; 244 | } 245 | 246 | acc->SaveCalFile(dsmId, devId); 247 | */ 248 | 249 | // New default behavior is to save all cards in one fell swoop. 250 | acc->SaveAllCalFiles(); 251 | } 252 | 253 | 254 | void AutoCalPage::createTree() 255 | { 256 | cout << "AutoCalPage::createTree" << endl; 257 | treeView = new QTreeView(); 258 | 259 | // extract the tree from AutoCalClient. 260 | cout << acc->GetTreeModel(); 261 | treeModel = new TreeModel( QString(acc->GetTreeModel().c_str()) ); 262 | 263 | // Initialize the QTreeView 264 | treeView->setModel(treeModel); 265 | treeView->expandAll(); 266 | treeView->setMinimumWidth(300); 267 | treeView->resizeColumnToContents(0); 268 | treeView->resizeColumnToContents(1); 269 | treeView->resizeColumnToContents(2); 270 | 271 | // The dsmId(s) and devId(s) are hidden in the 3rd column. 272 | // treeView->hideColumn(2); 273 | 274 | connect(treeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), 275 | this, SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))); 276 | 277 | treeView->setCurrentIndex(treeView->model()->index(0,0)); 278 | } 279 | 280 | 281 | void AutoCalPage::selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/) 282 | { 283 | if (selected.indexes().count() == 0) 284 | return; 285 | 286 | QModelIndex index = selected.indexes().first(); 287 | QModelIndex parent = index.parent(); 288 | 289 | if (parent == QModelIndex()) { 290 | dsmId = devId; 291 | return; 292 | } 293 | QModelIndex devIdx = index.sibling(index.row(), 2); 294 | devId = treeModel->data(devIdx, Qt::DisplayRole).toInt(); 295 | 296 | QModelIndex dsmIdx = parent.sibling(parent.row(), 2); 297 | dsmId = treeModel->data(dsmIdx, Qt::DisplayRole).toInt(); 298 | 299 | for (int chn = 0; chn < numA2DChannels; chn++) { 300 | VarName[chn]->setText( QString( acc->GetVarName(dsmId, devId, chn).c_str() ) ); 301 | 302 | OldTimeStamp[chn]->setText( QString( acc->GetOldTimeStamp(dsmId, devId, chn).c_str() ) ); 303 | NewTimeStamp[chn]->setText( QString( acc->GetNewTimeStamp(dsmId, devId, chn).c_str() ) ); 304 | 305 | OldTemperature[chn]->setText( QString::number( acc->GetOldTemperature(dsmId, devId, chn) ) ); 306 | NewTemperature[chn]->setText( QString::number( acc->GetNewTemperature(dsmId, devId, chn) ) ); 307 | 308 | OldIntcp[chn]->setText( QString::number( acc->GetOldCals(dsmId, devId, chn)[0] ) ); 309 | NewIntcp[chn]->setText( QString::number( acc->GetNewCals(dsmId, devId, chn)[0] ) ); 310 | 311 | OldSlope[chn]->setText( QString::number( acc->GetOldCals(dsmId, devId, chn)[1] ) ); 312 | NewSlope[chn]->setText( QString::number( acc->GetNewCals(dsmId, devId, chn)[1] ) ); 313 | } 314 | } 315 | 316 | 317 | void AutoCalPage::createGrid() 318 | { 319 | gridGroupBox = new QGroupBox(tr("Auto Cal Results")); 320 | 321 | QGridLayout *layout = new QGridLayout; 322 | 323 | ChannelTitle = new QLabel( QString( "CHN" ) ); 324 | VarNameTitle = new QLabel( QString( "VARNAME" ) ); 325 | TimeStampTitle = new QLabel( QString( "TIME" ) ); 326 | IntcpTitle = new QLabel( QString( "INTCP" ) ); 327 | SlopeTitle = new QLabel( QString( "SLOPE" ) ); 328 | 329 | TemperatureTitle = new QLabel( QString( "TEMP" ) ); 330 | 331 | QFont font; 332 | #if defined(Q_WS_X11) 333 | font.setFamily("Monospace"); 334 | #else 335 | font.setFamily("Courier New"); 336 | #endif 337 | font.setPointSize(9); 338 | setFont(font); 339 | 340 | layout->addWidget( ChannelTitle, 0, 0); 341 | layout->addWidget( VarNameTitle, 0, 1); 342 | layout->addWidget( TimeStampTitle, 0, 2); 343 | layout->addWidget( IntcpTitle, 0, 3); 344 | layout->addWidget( SlopeTitle, 0, 4); 345 | 346 | layout->setColumnMinimumWidth( 2, 174); 347 | 348 | // layout->addWidget( TemperatureTitle, 0, 2); 349 | 350 | for (int chn = 0; chn < numA2DChannels; chn++) { 351 | Channel[chn] = new QLabel( QString("%1:").arg(chn) ); 352 | layout->addWidget(Channel[chn], chn*3+2, 0, 2, 1); 353 | 354 | VarName[chn] = new QLabel; 355 | layout->addWidget(VarName[chn], chn*3+2, 1, 2, 1); 356 | 357 | // layout->setRowMinimumHeight(chn*2, 30); 358 | 359 | OldTimeStamp[chn] = new QLabel; 360 | OldTemperature[chn] = new QLabel; 361 | OldIntcp[chn] = new QLabel; 362 | OldSlope[chn] = new QLabel; 363 | 364 | NewTimeStamp[chn] = new QLabel; 365 | NewTemperature[chn] = new QLabel; 366 | NewIntcp[chn] = new QLabel; 367 | NewSlope[chn] = new QLabel; 368 | 369 | // add a "blank line" between channels 370 | layout->addWidget(new QLabel, (chn*3)+1, 0, 1, 5); 371 | 372 | layout->addWidget(OldTimeStamp[chn], (chn*3)+2, 2); 373 | layout->addWidget(OldIntcp[chn], (chn*3)+2, 3); 374 | layout->addWidget(OldSlope[chn], (chn*3)+2, 4); 375 | 376 | layout->addWidget(NewTimeStamp[chn], (chn*3)+3, 2); 377 | layout->addWidget(NewIntcp[chn], (chn*3)+3, 3); 378 | layout->addWidget(NewSlope[chn], (chn*3)+3, 4); 379 | } 380 | // layout->addWidget(OldTemperature[chn], (chn*3)+2, 2); 381 | // layout->addWidget(NewTemperature[chn], (chn*3)+3, 2); 382 | 383 | // gridGroupBox->setMinimumSize(450, 600); // width, height 384 | gridGroupBox->setLayout(layout); 385 | } 386 | 387 | 388 | void AutoCalPage::initializePage() 389 | { 390 | cout << "AutoCalPage::initializePage" << endl; 391 | 392 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 393 | if (calibrator->setup("acserver", "cal")) return; 394 | QApplication::restoreOverrideCursor(); 395 | 396 | createTree(); 397 | createGrid(); 398 | 399 | mainLayout = new QHBoxLayout; 400 | mainLayout->addWidget(treeView); 401 | mainLayout->addWidget(gridGroupBox); 402 | 403 | setLayout(mainLayout); 404 | 405 | qPD = new QProgressDialog(this); 406 | qPD->setRange(0, acc->maxProgress() ); 407 | qPD->setWindowTitle(tr("Auto Calibrating...")); 408 | qPD->setWindowModality(Qt::WindowModal); 409 | 410 | // This connection spans across threads so it is a 411 | // Qt::QueuedConnection by default. 412 | // (http://doc.qt.nokia.com/4.6/threads-mandelbrot.html) 413 | connect(acc, SIGNAL(errMessage(const QString&)), 414 | this, SLOT(errMessage(const QString&))); 415 | 416 | connect(calibrator, SIGNAL(setValue(int)), 417 | qPD, SLOT(setValue(int)) ); 418 | 419 | connect(qPD, SIGNAL(canceled()), 420 | calibrator, SLOT(cancel()) ); 421 | 422 | calibrator->start(); // see Calibrator::run 423 | } 424 | 425 | 426 | void AutoCalPage::errMessage(const QString& message) 427 | { 428 | QMessageBox::warning(this, "error", message); 429 | } 430 | 431 | 432 | void AutoCalPage::setValue(int progress) 433 | { 434 | qPD->setValue(progress); 435 | }; 436 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | -------------------------------------------------------------------------------- /AutoCalClient.cc: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4; -*- */ 2 | /* vim: set shiftwidth=4 softtabstop=4 expandtab: */ 3 | /* 4 | ******************************************************************** 5 | ** NIDAS: NCAR In-situ Data Acquistion Software 6 | ** 7 | ** 2010, Copyright University Corporation for Atmospheric Research 8 | ** 9 | ** This program is free software; you can redistribute it and/or modify 10 | ** it under the terms of the GNU General Public License as published by 11 | ** the Free Software Foundation; either version 2 of the License, or 12 | ** (at your option) any later version. 13 | ** 14 | ** This program is distributed in the hope that it will be useful, 15 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ** GNU General Public License for more details. 18 | ** 19 | ** The LICENSE.txt file accompanying this software contains 20 | ** a copy of the GNU General Public License. If it is not found, 21 | ** write to the Free Software Foundation, Inc., 22 | ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | ** 24 | ******************************************************************** 25 | */ 26 | #include "AutoCalClient.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | //#include 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | 47 | typedef unsigned char uchar; 48 | 49 | #define TDELAY 10 // time delay after setting a new voltage (seconds) 50 | 51 | using namespace XmlRpc; 52 | namespace n_u = nidas::util; 53 | 54 | string fillStateDesc[] = {"SKIP", "PEND", "EMPTY", "FULL" }; 55 | 56 | AutoCalClient::AutoCalClient(): 57 | nLevels(0), 58 | progress(1), 59 | testVoltage(false), 60 | idxVltLvl(-1), 61 | VltLvl(0) 62 | { 63 | list volts; 64 | voltageLevels["--"] = volts; 65 | 66 | volts.push_back(0); 67 | volts.push_back(2); 68 | voltageLevels["gpDAQ"] = volts; 69 | 70 | volts.clear(); 71 | volts.push_back(0); 72 | volts.push_back(1); 73 | volts.push_back(5); 74 | voltageLevels["dmmat"] = volts; 75 | voltageLevels["4F"] = volts; 76 | voltageLevels["2T"] = volts; 77 | 78 | volts.push_back(10); 79 | voltageLevels["2F"] = volts; 80 | 81 | volts.push_back(-10); 82 | voltageLevels["1T"] = volts; 83 | 84 | volts.clear(); 85 | volts.push_back(-99); 86 | volts.push_back(0); 87 | volts.push_back(1); 88 | volts.push_back(2); 89 | volts.push_back(5); 90 | volts.push_back(10); 91 | volts.push_back(-10); 92 | voltageLevels["XX"] = volts; 93 | }; 94 | 95 | 96 | string AutoCalClient::ChnSetDesc(unsigned int val) { 97 | ostringstream ostr; 98 | for (int i=7; i>-1; i--) { 99 | ostr << ((val & (1<0) ostr << " "; 101 | } 102 | return ostr.str(); 103 | } 104 | 105 | 106 | void AutoCalClient::setTestVoltage(int dsmId, int devId) 107 | { 108 | testVoltage = true; 109 | tvDsmId = dsmId; 110 | tvDevId = devId; 111 | }; 112 | 113 | 114 | bool AutoCalClient::readCalFile(DSMSensor* sensor, string card) 115 | { 116 | std::cout << "AutoCalClient::readCalFile(" << sensor->getDSMName() << ":" << sensor->getDeviceName() << ")" << std::endl; 117 | uint dsmId = sensor->getDSMId(); 118 | uint devId = sensor->getSensorId(); 119 | int N = devNchannels[id(dsmId, devId)]; 120 | 121 | dsm_time_t sysTime, calTime = 0; 122 | ostringstream ostr; 123 | 124 | // pre-fill with '0' in case a calFile is missing an entry 125 | // create unused (gain bplr) entries for (1 0) and (4 1) anyway 126 | for (int gain = 0; gain < 3; gain++) { 127 | for (int bplr = 0; bplr < 2; bplr++) { 128 | calFileTime[dsmId][devId][1<& cfs = sensor->getCalFiles(); 139 | if (cfs.empty()) { 140 | ostr << "CalFile not set for..." << std::endl; 141 | ostr << "DSM: " << sensor->getDSMName() << " device: " << sensor->getDeviceName() << std::endl; 142 | std::cout << ostr.str() << std::endl; 143 | QMessageBox::warning(0, "CalFile ERROR", ostr.str().c_str()); 144 | return true; 145 | } 146 | CalFile *cf = cfs.begin()->second; 147 | 148 | // extract the A2D board serial number from its CalFile 149 | calFilePath[dsmId][devId] = 150 | Project::getInstance()->expandString( cf->getPath() ); 151 | calFileName[dsmId][devId] = cf->getFile(); 152 | 153 | ostr << "calFilePath: " << calFilePath[dsmId][devId] << std::endl; 154 | ostr << "calFileName: " << calFileName[dsmId][devId] << std::endl; 155 | 156 | // get system time 157 | struct timeval tv; 158 | ::gettimeofday(&tv,0); 159 | sysTime = (dsm_time_t)tv.tv_sec * USECS_PER_SEC + tv.tv_usec; 160 | 161 | // Read CalFile containing the following fields after the timeStamp 162 | // gain bipolar(1=true,0=false) intcp0 slope0 intcp1 slope1 ... intcp7 slope7 163 | // gpDAQ has four cals.... 164 | int nCals; 165 | if (card == "gpDAQ") 166 | nCals = 4; // gpDAQ uses 3rd order cal 167 | else 168 | nCals = 2; // all else are mx+b 169 | int nd = 2 + N * nCals; 170 | float d[nd]; 171 | while (sysTime >= cf->nextTime().toUsecs()) 172 | { 173 | try { 174 | n_u::UTime ut; 175 | int n = cf->readCF(ut, d, nd); 176 | calTime = ut.toUsecs(); 177 | if (n < 2) continue; 178 | 179 | int gain = (int)d[0]; 180 | int bplr = (int)d[1]; 181 | 182 | calFileTime[dsmId][devId][gain][bplr] = calTime; 183 | // This does not coorectly push_back 4 cals. 184 | for (int i = 0; i < std::min((n-2)/nCals, N); i++) { 185 | calFileCals[dsmId][devId][i][gain][bplr].clear(); 186 | calFileCals[dsmId][devId][i][gain][bplr].push_back(d[2+i*nCals]); 187 | calFileCals[dsmId][devId][i][gain][bplr].push_back(d[3+i*nCals]); 188 | } 189 | } 190 | catch(const n_u::EOFException& e) 191 | { 192 | ostr << e.what(); 193 | std::cout << ostr.str() << std::endl; 194 | QMessageBox::warning(0, "CalFile ERROR", ostr.str().c_str()); 195 | break; 196 | } 197 | catch(const n_u::IOException& e) 198 | { 199 | ostr << e.what(); 200 | std::cout << ostr.str() << std::endl; 201 | QMessageBox::warning(0, "CalFile ERROR", ostr.str().c_str()); 202 | break; 203 | } 204 | catch(const n_u::ParseException& e) 205 | { 206 | ostr << e.what(); 207 | std::cout << ostr.str() << std::endl; 208 | QMessageBox::warning(0, "CalFile ERROR", ostr.str().c_str()); 209 | break; 210 | } 211 | } 212 | // re-opening CalFile 213 | if (cf->eof()) { 214 | cf->close(); 215 | cf->open(); 216 | } 217 | return false; 218 | } 219 | 220 | 221 | a2d_setup AutoCalClient::GetA2dSetup(int dsmId, int devId) 222 | { 223 | a2d_setup setup; 224 | int nChannels = 0; 225 | 226 | string dsmName = dsmNames[dsmId]; 227 | string devName = devNames[id(dsmId, devId)]; 228 | std::cout << "AutoCalClient::GetA2dSetup dsmName: " << dsmName << " devName: " << devName << std::endl; 229 | 230 | #ifndef SIMULATE 231 | // establish an xmlrpc connection to this DSM 232 | XmlRpcClient dsm_xmlrpc_client(dsmName.c_str(), 233 | DSM_XMLRPC_PORT_TCP, "/RPC2"); 234 | 235 | // fetch the current setup from the card itself 236 | XmlRpcValue get_params, get_result; 237 | get_params["device"] = devName; 238 | get_params["action"] = "getA2DSetup"; 239 | std::cout << " get_params: " << get_params.toXml() << std::endl; 240 | 241 | if (dsm_xmlrpc_client.execute("SensorAction", get_params, get_result)) { 242 | if (dsm_xmlrpc_client.isFault()) { 243 | ostringstream ostr; 244 | ostr << get_result["faultString"] << std::endl; 245 | ostr << "ignoring: " << dsmName << ":" << devName; 246 | std::cout << ostr.str() << std::endl; 247 | QMessageBox::warning(0, "xmlrpc client fault", ostr.str().c_str()); 248 | 249 | dsm_xmlrpc_client.close(); 250 | return setup; 251 | } 252 | dsm_xmlrpc_client.close(); 253 | nChannels = get_result["nChannels"]; 254 | for (int i = 0; i < nChannels; i++) { 255 | setup.gain[i] = get_result["gain"][i]; 256 | setup.offset[i] = get_result["offset"][i]; 257 | setup.calset[i] = get_result["calset"][i]; 258 | } 259 | setup.vcal = get_result["vcal"]; 260 | } 261 | else { 262 | std::cout << "xmlrpc client NOT responding" << std::endl; 263 | } 264 | #else 265 | for (int i = 0; i < nChannels; i++) { 266 | setup.gain[i] = Gains[dsmId][devId][i]; 267 | setup.offset[i] = Bplrs[dsmId][devId][i]; 268 | setup.calset[i] = 1; 269 | } 270 | setup.vcal = -99; 271 | #endif 272 | return setup; 273 | } 274 | 275 | 276 | void AutoCalClient::TestVoltage(int channel, int level) 277 | { 278 | std::cout << "AutoCalClient::TestVoltage " 279 | << dsmNames[tvDsmId] << ":" << devNames[id(tvDsmId, tvDevId)] << ":" 280 | << ChnSetDesc(1 << channel) << ":" << level << "v" << std::endl; 281 | 282 | #ifndef SIMULATE 283 | XmlRpcClient dsm_xmlrpc_client(dsmNames[tvDsmId].c_str(), 284 | DSM_XMLRPC_PORT_TCP, "/RPC2"); 285 | #endif 286 | 287 | XmlRpcValue set_params, set_result; 288 | set_params["device"] = devNames[id(tvDsmId, tvDevId)]; 289 | set_params["action"] = "testVoltage"; 290 | set_params["state"] = 1; 291 | set_params["voltage"] = level; 292 | set_params["calset"] = (1 << channel); 293 | 294 | std::cout << " set_params: " << set_params.toXml() << std::endl; 295 | 296 | #ifndef SIMULATE 297 | // Instruct card to generate a calibration voltage. 298 | if (dsm_xmlrpc_client.execute("SensorAction", set_params, set_result)) { 299 | if (dsm_xmlrpc_client.isFault()) { 300 | std::cout << "xmlrpc client fault: " << set_result["faultString"] << std::endl; 301 | } 302 | } 303 | else { 304 | std::cout << "xmlrpc client NOT responding" << std::endl; 305 | } 306 | dsm_xmlrpc_client.close(); 307 | std::cout << "set_result: " << set_result.toXml() << std::endl; 308 | #endif 309 | emit updateSelection(); 310 | } 311 | 312 | 313 | bool AutoCalClient::Setup(DSMSensor* sensor) 314 | { 315 | std::cout << "AutoCalClient::Setup(" << sensor->getDSMName() << ":" << sensor->getDeviceName() << ")" << std::endl; 316 | 317 | string dsmName = sensor->getDSMName(); 318 | string devName = sensor->getDeviceName(); 319 | 320 | uint dsmId = sensor->getDSMId(); 321 | uint devId = sensor->getSensorId(); 322 | 323 | #ifndef SIMULATE 324 | // establish an xmlrpc connection to this DSM 325 | XmlRpcClient dsm_xmlrpc_client(dsmName.c_str(), 326 | DSM_XMLRPC_PORT_TCP, "/RPC2"); 327 | 328 | // fetch the current setup from the card itself 329 | XmlRpcValue get_params, get_result; 330 | get_params["device"] = devName; 331 | get_params["action"] = "getA2DSetup"; 332 | std::cout << " get_params: " << get_params.toXml() << std::endl; 333 | a2d_setup setup; 334 | int nChannels = 0; 335 | 336 | try { 337 | if (dsm_xmlrpc_client.execute("SensorAction", get_params, get_result)) { 338 | if (dsm_xmlrpc_client.isFault()) { 339 | ostringstream ostr; 340 | ostr << get_result["faultString"] << std::endl; 341 | ostr << "ignoring: " << dsmName << ":" << devName; 342 | QMessageBox::warning(0, "xmlrpc client fault", ostr.str().c_str()); 343 | 344 | dsm_xmlrpc_client.close(); 345 | return true; 346 | } 347 | dsm_xmlrpc_client.close(); 348 | nChannels = get_result["nChannels"]; 349 | for (int i = 0; i < nChannels; i++) { 350 | setup.gain[i] = get_result["gain"][i]; 351 | setup.offset[i] = get_result["offset"][i]; 352 | setup.calset[i] = get_result["calset"][i]; 353 | } 354 | setup.vcal = get_result["vcal"]; 355 | #ifdef DONT_IGNORE_ACTIVE_CARDS 356 | if (setup.vcal != -99) { 357 | // TODO ensure that a -99 is reported back by the driver when nothing is active. 358 | ostringstream ostr; 359 | ostr << "A calibration voltage is active here. Cannot auto calibrate this." << std::endl; 360 | ostr << "ignoring: " << dsmName << ":" << devName; 361 | std::cout << ostr.str() << std::endl; 362 | QMessageBox::warning(0, "card is busy", ostr.str().c_str()); 363 | return true; 364 | } 365 | #endif 366 | } 367 | else { 368 | ostringstream ostr; 369 | ostr << get_result["faultString"] << std::endl; 370 | ostr << "ignoring: " << dsmName << ":" << devName; 371 | std::cout << ostr.str() << std::endl; 372 | // QMessageBox::warning(0, "xmlrpc client NOT responding", ostr.str().c_str()); 373 | return true; 374 | } 375 | } 376 | catch (XmlRpc::XmlRpcException& e) 377 | { 378 | std::cout << "(" << e.getCode() << ") " << e.getMessage() << std::endl; 379 | return true; 380 | } 381 | std::cout << "get_result: " << get_result.toXml() << std::endl; 382 | #endif 383 | 384 | /* Parse XML for this sensor, validate against info returned from dsm/class above. 385 | * Setup card on this end. 386 | */ 387 | list& tags = sensor->getSampleTags(); 388 | list::const_iterator ti; 389 | string card = get_result["card"]; 390 | for (ti = tags.begin(); ti != tags.end(); ++ti) { 391 | SampleTag* tag = *ti; 392 | 393 | dsm_sample_id_t sampId = tag->getId(); 394 | std::cout << "sampId: " << sampId << std::endl; 395 | if (sampId == 0) std::cout << "(sampId == 0)" << std::endl; 396 | 397 | uint varId = 0; 398 | for (unsigned int vi = 0; vi < tag->getVariables().size(); vi++) { 399 | Variable& var = tag->getVariable(vi); 400 | 401 | // Disable engineering calibration 402 | var.setConverter(0); 403 | 404 | int chan = var.getA2dChannel(); 405 | if (chan < 0) { 406 | temperatureId[dsmId][devId] = sampId; 407 | break; 408 | } 409 | uint channel = chan; 410 | 411 | int gain=1, bplr=0; 412 | 413 | const Parameter * parm; 414 | if (card == "gpDAQ") { 415 | parm = var.getParameter("ifsr"); 416 | if (parm) 417 | // +1 sort of a hack. ifsr=0==gain=1, ifsr=1==gain=2 418 | gain = (int)parm->getNumericValue(0) + 1; 419 | 420 | parm = var.getParameter("ipol"); 421 | if (parm) 422 | // also sort of a hack. 423 | bplr = 1 - (int)(parm->getNumericValue(0)); 424 | } 425 | else { 426 | parm = var.getParameter("gain"); 427 | if (parm) 428 | gain = (int)parm->getNumericValue(0); 429 | 430 | parm = var.getParameter("bipolar"); 431 | if (parm) 432 | bplr = (int)(parm->getNumericValue(0)); 433 | } 434 | #ifndef SIMULATE 435 | // compare with what is currently configured 436 | // I don't understand why returned bipolar is opposite of cfg'd. --cjw Oct2021 437 | if ( (setup.gain[channel] != gain) || (setup.offset[channel] != !bplr) ) { 438 | ostringstream ostr; 439 | ostr << "can not calibrate channel " << channel << " because it is running as: " 440 | << setup.gain[channel] << (setup.offset[channel] ? "F" : "T") 441 | << " but configured as: " 442 | << gain << (bplr ? "T" : "F") << std::endl 443 | << "(you need to reboot this DSM)" << std::endl 444 | << "ignoring: " << dsmName << ":" << devName; 445 | std::cout << ostr.str() << std::endl; 446 | QMessageBox::warning(0, "miss-configured card", ostr.str().c_str()); 447 | return true; 448 | } 449 | #endif 450 | // channel is available 451 | ostringstream gb; 452 | if (card == "gpDAQ") 453 | gb << card; 454 | else 455 | gb << gain << (bplr ? "T" : "F"); 456 | 457 | sampleInfo[sampId].channel[varId++] = channel; 458 | 459 | timeStamp[dsmId][devId][channel] = 0; 460 | VarNames[dsmId][devId][channel] = var.getName(); 461 | Gains[dsmId][devId][channel] = gain; 462 | Bplrs[dsmId][devId][channel] = bplr; 463 | 464 | std::cout << "AutoCalClient::Setup channel: " << channel << " gain: " << gain << " bplr: " << bplr << std::endl; 465 | 466 | list::iterator l; 467 | for ( l = voltageLevels[gb.str()].begin(); l != voltageLevels[gb.str()].end(); l++) { 468 | 469 | calActv[*l][dsmId][devId][channel] = PEND; 470 | calData[dsmId][devId][channel][*l].reserve( NSAMPS * sizeof(float) ); 471 | 472 | std::cout << sampId; 473 | std::cout << " CcalActv[" << *l << "][" << dsmId << "][" << devId << "][" << channel << "] = "; 474 | std::cout << fillStateDesc[ calActv[*l][dsmId][devId][channel] ] << std::endl; 475 | 476 | if (slowestRate[*l] == 0) 477 | slowestRate[*l] = UINT_MAX; 478 | 479 | if (slowestRate[*l] > tag->getRate()) 480 | slowestRate[*l] = (uint) tag->getRate(); 481 | } 482 | if (nLevels < voltageLevels[gb.str()].size()) 483 | nLevels = voltageLevels[gb.str()].size(); 484 | std::cout << "nLevels: " << nLevels << std::endl; 485 | } 486 | sampleInfo[sampId].dsmId = dsmId; 487 | sampleInfo[sampId].devId = devId; 488 | sampleInfo[sampId].rate = (uint) tag->getRate(); 489 | sampleInfo[sampId].isaTemperatureId = false; 490 | sampleInfo[temperatureId[dsmId][devId]].isaTemperatureId = true; 491 | temperatureData[dsmId][devId].reserve( NSAMPS * sizeof(float) ); 492 | 493 | std::cout << "sampleInfo[" << sampId << "].rate: " << sampleInfo[sampId].rate << std::endl; 494 | } 495 | 496 | dsmNames[dsmId] = dsmName; 497 | devNames[id(dsmId, devId)] = devName; 498 | devNchannels[id(dsmId, devId)] = nChannels; 499 | cardType[id(dsmId, devId)] = card; 500 | lastTimeStamp = 0; 501 | 502 | readCalFile(sensor, card); 503 | 504 | list::iterator l; 505 | for ( l = voltageLevels["1T"].begin(); l != voltageLevels["1T"].end(); l++) 506 | std::cout << "slowestRate[" << *l << "]: " << slowestRate[*l] << std::endl; 507 | 508 | return false; 509 | } 510 | 511 | 512 | void AutoCalClient::createQtTreeModel( mapdsmLocations ) 513 | { 514 | // clear out the previous model description 515 | QTreeModel.str(""); 516 | 517 | // for each level 518 | for (iLevel = calActv.begin(); 519 | iLevel != calActv.end(); iLevel++) { 520 | 521 | int level = iLevel->first; 522 | dsm_a_type* Dsms = &(iLevel->second); 523 | 524 | // Define QTreeModel with the '0' voltage level since all channels measure it. 525 | if (level != 0) continue; 526 | 527 | // for each DSM 528 | for (iDsm = Dsms->begin(); 529 | iDsm != Dsms->end(); iDsm++) { 530 | 531 | uint dsmId = iDsm->first; 532 | device_a_type* Devices = &(iDsm->second); 533 | 534 | QTreeModel << dsmLocations[dsmId] << "\t" << dsmNames[dsmId] << "\t" << dsmId << "\n"; 535 | 536 | // for each device 537 | for (iDevice = Devices->begin(); 538 | iDevice != Devices->end(); iDevice++) { 539 | 540 | uint devId = iDevice->first; 541 | 542 | string calFile; 543 | if ( calFileName[dsmId][devId].empty() ) 544 | calFile = "---"; 545 | else 546 | calFile = calFileName[dsmId][devId]; 547 | 548 | QTreeModel << " " << calFile << "\t" << devNames[id(dsmId, devId)] << "\t" << devId << "\n"; 549 | } 550 | } 551 | } 552 | // start the Voltage level index 553 | iLevel = calActv.begin(); 554 | } 555 | 556 | 557 | // This is a re-entrant function that advances to the next calibration voltage level. 558 | // It iterates across the levels, dsmNames, devNames, and Channels. 559 | // 560 | enum stateEnum AutoCalClient::SetNextCalVoltage(enum stateEnum state) 561 | { 562 | if (state == DONE) { 563 | // Point to voltage level that is common to all voltage ranges by advancing 564 | // once past the beginning. If the range begins with '-10' then '0' is next. 565 | // Or if the range begins with '0' then '1' is next. Both '0' and '1' are common 566 | // voltage levels. 567 | iLevel = calActv.begin(); 568 | iLevel++; 569 | std::cout << __PRETTY_FUNCTION__ << " DONE state... clearing all DSM's channels\n"; 570 | } 571 | std::cout << "AutoCalClient::SetNextCalVoltage" << std::endl; 572 | 573 | if (iLevel == calActv.end() ) { 574 | iLevel = calActv.begin(); 575 | state = DONE; 576 | } 577 | bool alive = false; 578 | int level = iLevel->first; 579 | dsm_a_type* Dsms = &(iLevel->second); 580 | std::cout << "SNCV " << level << std::endl; 581 | VltLvl = level; 582 | 583 | // for each DSM 584 | for (iDsm = Dsms->begin(); 585 | iDsm != Dsms->end(); iDsm++) { 586 | 587 | uint dsmId = iDsm->first; 588 | device_a_type* Devices = &(iDsm->second); 589 | std::cout << " " << dsmId << std::endl; 590 | 591 | // for each device 592 | for (iDevice = Devices->begin(); 593 | iDevice != Devices->end(); iDevice++) { 594 | 595 | uint devId = iDevice->first; 596 | channel_a_type* Channels = &(iDevice->second); 597 | std::cout << " " << devId << std::endl; 598 | 599 | #ifndef SIMULATE 600 | XmlRpcClient dsm_xmlrpc_client(dsmNames[dsmId].c_str(), 601 | DSM_XMLRPC_PORT_TCP, "/RPC2"); 602 | #endif 603 | 604 | XmlRpcValue set_params, set_result; 605 | set_params["device"] = devNames[id(dsmId, devId)]; 606 | set_params["action"] = "testVoltage"; 607 | uchar ChnSet = 0; 608 | 609 | if (state == DONE) { 610 | std::cout << "leaving cal voltages and channels in an open state" << std::endl; 611 | set_params["state"] = 0; 612 | set_params["voltage"] = 0; 613 | ChnSet = 0xff; 614 | } else { 615 | set_params["state"] = 1; 616 | set_params["voltage"] = level; 617 | 618 | // for each channel 619 | for (iChannel = Channels->begin(); 620 | iChannel != Channels->end(); iChannel++) { 621 | 622 | uint channel = iChannel->first; 623 | 624 | ChnSet |= (1 << channel); 625 | calActv[level][dsmId][devId][channel] = EMPTY; 626 | 627 | std::cout << " "; 628 | std::cout << "ScalActv[" << level << "][" << dsmId << "][" << devId << "][" << channel << "] = "; 629 | std::cout << fillStateDesc[ calActv[level][dsmId][devId][channel] ] << std::endl; 630 | } 631 | } 632 | std::cout << " "; 633 | std::cout << "XMLRPC ChnSet: " << ChnSetDesc(ChnSet) << std::endl; 634 | set_params["calset"] = ChnSet; 635 | 636 | #ifndef SIMULATE 637 | std::cout << " set_params: " << set_params.toXml() << std::endl; 638 | 639 | // Instruct card to generate a calibration voltage. 640 | if (dsm_xmlrpc_client.execute("SensorAction", set_params, set_result)) { 641 | if (dsm_xmlrpc_client.isFault()) { 642 | std::cout << "xmlrpc client fault: " << set_result["faultString"] << std::endl; 643 | 644 | // skip other cards owned by this DSM 645 | dsm_xmlrpc_client.close(); 646 | break; 647 | } 648 | } 649 | else { 650 | std::cout << "xmlrpc client NOT responding" << std::endl; 651 | 652 | // skip other cards owned by this DSM 653 | dsm_xmlrpc_client.close(); 654 | break; 655 | } 656 | dsm_xmlrpc_client.close(); 657 | std::cout << "set_result: " << set_result.toXml() << std::endl; 658 | #endif 659 | alive = true; 660 | } 661 | } 662 | struct timeval tv; 663 | ::gettimeofday(&tv,0); 664 | lastTimeStamp = (dsm_time_t)tv.tv_sec * USECS_PER_SEC + tv.tv_usec; 665 | 666 | if (!alive) return DEAD; 667 | 668 | // re-entrant for each level 669 | iLevel++; 670 | idxVltLvl++; 671 | 672 | return state; 673 | } 674 | 675 | 676 | bool AutoCalClient::receive(const Sample* samp) throw() 677 | { 678 | dsm_time_t currTimeStamp; 679 | static int prevSecond; 680 | 681 | struct timeval tv; 682 | ::gettimeofday(&tv,0); 683 | currTimeStamp = samp->getTimeTag(); 684 | 685 | #ifndef SIMULATE 686 | if (currTimeStamp < lastTimeStamp + TDELAY * USECS_PER_SEC) 687 | return false; 688 | #endif 689 | 690 | dsm_sample_id_t sampId = samp->getId(); 691 | uint dsmId = sampleInfo[sampId].dsmId; 692 | uint devId = sampleInfo[sampId].devId; 693 | 694 | if (dsmId == 0) { std::cout << "dsmId == 0\n"; return false; } 695 | if (devId == 0) { std::cout << "devId == 0\n"; return false; } 696 | 697 | // std::cout << n_u::UTime(currTimeStamp).format(true,"%Y %b %d %H:%M:%S") << std::endl; 698 | // std::cout << " AutoCalClient::receive " << sampId << " [" << VltLvl << "][" << dsmId << "][" << devId << "]" << std::endl; 699 | 700 | const float* fp = 701 | (const float*) samp->getConstVoidDataPtr(); 702 | 703 | // store the card's onboard temperatureData 704 | // There is only one variable in this sample. 705 | if (sampleInfo[sampId].isaTemperatureId ) { 706 | 707 | // stop gathering after NSAMPS received 708 | if (temperatureData[dsmId][devId].size() > NSAMPS-1) 709 | return true; 710 | 711 | // std::cout << "RtemperatureData[" << dsmId << "][" << devId << "].size() = "; 712 | // std::cout << temperatureData[dsmId][devId].size(); 713 | 714 | temperatureData[dsmId][devId].push_back(fp[0]); 715 | 716 | // std::cout << " " << temperatureData[dsmId][devId].size() << std::endl; 717 | return true; 718 | } 719 | bool channelFound = false; 720 | // store the card's generated calibration 721 | // There are one or more variables in this sample. 722 | for (uint varId = 0; varId < samp->getDataByteLength()/sizeof(float); varId++) { 723 | 724 | uint channel = sampleInfo[sampId].channel[varId]; 725 | 726 | // remember the latest measured value for test display 727 | testData[dsmId][devId][channel] = fp[varId]; 728 | 729 | // ignore samples that are not currently being gathered 730 | if ( calActv[VltLvl][dsmId][devId][channel] != EMPTY ) 731 | continue; 732 | 733 | channelFound = true; 734 | 735 | // when testing in manual mode, don't gather data. 736 | if ( testVoltage ) continue; 737 | 738 | // timetag first data value received 739 | if (timeStamp[dsmId][devId][channel] == 0) 740 | timeStamp[dsmId][devId][channel] = currTimeStamp; 741 | 742 | #ifdef SIMULATE 743 | calData[dsmId][devId][channel][VltLvl].push_back((double)VltLvl + ((channel+1) * 0.1) ); 744 | #else 745 | calData[dsmId][devId][channel][VltLvl].push_back(fp[varId]); 746 | #endif 747 | 748 | int size = calData[dsmId][devId][channel][VltLvl].size(); 749 | 750 | // stop gathering after NSAMPS received 751 | if (size > NSAMPS-1) 752 | calActv[VltLvl][dsmId][devId][channel] = FULL; 753 | 754 | // The progress bar to exhibits the rate of the slowest 755 | // channel gathered at the current voltage level. 756 | if (size) 757 | if (sampleInfo[sampId].rate == slowestRate[VltLvl]) 758 | progress = idxVltLvl * NSAMPS + size; 759 | 760 | // std::cout << n_u::UTime(currTimeStamp).format(true,"%Y %b %d %H:%M:%S "); 761 | // std::cout << " progress: " << progress; 762 | // std::cout << " sampId: " << sampId; 763 | // std::cout << " value: " << setw(10) << fp[varId]; 764 | // std::cout << " RcalData[" << dsmId << "][" << devId << "][" << channel << "][" << VltLvl << "].size() = "; 765 | // std::cout << size << std::endl; 766 | } 767 | // instruct the test voltage wizard page to update its display 768 | if ( testVoltage && ( prevSecond != tv.tv_sec ) ) 769 | emit dispVolts(); 770 | 771 | prevSecond = tv.tv_sec; 772 | 773 | if ( !channelFound ) 774 | return false; 775 | 776 | return true; 777 | } 778 | 779 | 780 | // This funcion checks to see if enough data was gathered. 781 | // 782 | bool AutoCalClient::Gathered() 783 | { 784 | bool isGathered = false; 785 | 786 | map::iterator iSI; 787 | for ( iSI = sampleInfo.begin(); 788 | iSI != sampleInfo.end(); iSI++ ) 789 | { 790 | struct sA2dSampleInfo *SI = &(iSI->second); 791 | if ( SI->isaTemperatureId ) continue; 792 | 793 | map::iterator iC; 794 | for ( iC = SI->channel.begin(); 795 | iC != SI->channel.end(); iC++ ) 796 | { 797 | uint channel = iC->second; 798 | enum fillState fillstate = calActv[VltLvl][SI->dsmId][SI->devId][channel]; 799 | 800 | if ( fillstate == FULL ) 801 | isGathered = true; 802 | else if ( fillstate == EMPTY ) 803 | return false; 804 | } 805 | } 806 | if (isGathered) 807 | std::cout << "AutoCalClient::Gathered" << std::endl; 808 | 809 | return isGathered; 810 | } 811 | 812 | 813 | void AutoCalClient::DisplayResults() 814 | { 815 | std::cout << "AutoCalClient::DisplayResults" << std::endl; 816 | 817 | #ifdef SIMULATE 818 | calData[23][220][3][0].pop_back(); 819 | calData[23][220][3][0].push_back(NAN); 820 | calData[25][200][2][5].pop_back(); 821 | calData[25][200][2][5].push_back(NAN); 822 | calData[25][200][1][-10].pop_back(); 823 | calData[25][200][1][-10].push_back(NAN); 824 | #endif 825 | 826 | // for each level 827 | for (iLevel = calActv.begin(); 828 | iLevel != calActv.end(); iLevel++) { 829 | 830 | int level = iLevel->first; 831 | dsm_a_type* Dsms = &(iLevel->second); 832 | std::cout << "" << level << std::endl; 833 | 834 | // for each DSM 835 | for (iDsm = Dsms->begin(); 836 | iDsm != Dsms->end(); iDsm++) { 837 | 838 | uint dsmId = iDsm->first; 839 | device_a_type* Devices = &(iDsm->second); 840 | std::cout << " " << dsmId << std::endl; 841 | 842 | // for each device 843 | for (iDevice = Devices->begin(); 844 | iDevice != Devices->end(); iDevice++) { 845 | 846 | uint devId = iDevice->first; 847 | channel_a_type* Channels = &(iDevice->second); 848 | std::cout << " " << devId << std::endl; 849 | 850 | // for each channel 851 | for (iChannel = Channels->begin(); 852 | iChannel != Channels->end(); iChannel++) { 853 | 854 | uint channel = iChannel->first; 855 | std::cout << " " << channel << std::endl; 856 | 857 | std::cout << "DcalActv[" << level << "][" << dsmId << "][" << devId << "][" << channel << "] = "; 858 | std::cout << fillStateDesc[ calActv[level][dsmId][devId][channel] ] << std::endl; 859 | } 860 | } 861 | } 862 | } 863 | 864 | std::cout << "...................................................................\n"; 865 | 866 | struct { int gain; int bplr; } GB[] = {{1,1},{2,0},{2,1},{4,0}}; 867 | 868 | dsm_d_type::iterator iiDsm; 869 | device_d_type::iterator iiDevice; 870 | channel_d_type::iterator iiChannel; 871 | level_d_type::iterator iiLevel; 872 | data_d_type::iterator iiData; 873 | 874 | vector voltageMin; 875 | vector voltageMax; 876 | 877 | // for each DSM 878 | for (iiDsm = calData.begin(); 879 | iiDsm != calData.end(); iiDsm++) { 880 | 881 | uint dsmId = iiDsm->first; 882 | device_d_type* Devices = &(iiDsm->second); 883 | 884 | // for each device 885 | for (iiDevice = Devices->begin(); 886 | iiDevice != Devices->end(); iiDevice++) { 887 | 888 | QString devErr; 889 | 890 | uint devId = iiDevice->first; 891 | channel_d_type* Channels = &(iiDevice->second); 892 | 893 | map c0; // indexed by channel 894 | map c1; // indexed by channel 895 | 896 | // detect bad internal cal voltages on a per board basis 897 | map detected; 898 | 899 | // for each channel 900 | for (iiChannel = Channels->begin(); 901 | iiChannel != Channels->end(); iiChannel++) { 902 | 903 | uint channel = iiChannel->first; 904 | level_d_type* Levels = &(iiChannel->second); 905 | 906 | double aVoltageLevel, aVoltageMean, aVoltageWeight; 907 | double aVoltageMin, aVoltageMax; 908 | vector voltageMean; 909 | vector voltageLevel; 910 | vector voltageWeight; 911 | 912 | // for each voltage level 913 | // NOTE these levels could be from for any (gain, bplr) range. 914 | for (iiLevel = Levels->begin(); 915 | iiLevel != Levels->end(); iiLevel++) { 916 | 917 | int level = iiLevel->first; 918 | data_d_type* Data = &(iiLevel->second); 919 | size_t nPts = Data->size(); 920 | std::cout << "nPts: " << nPts << std::endl; 921 | 922 | // alert user of any out of bound values 923 | for (iiData = Data->begin(); iiData != Data->end(); iiData++) 924 | if (isnan(*iiData)) 925 | if (isNAN[dsmId][devId][channel][level] == false) { 926 | isNAN[dsmId][devId][channel][level] = true; 927 | 928 | QTextStream cout(stdout, QIODevice::WriteOnly); 929 | QString qstr; 930 | QTextStream(&qstr) << QString::fromStdString(dsmNames[dsmId]) << ":"; 931 | QTextStream(&qstr) << QString::fromStdString(devNames[id(dsmId, devId)]); 932 | QTextStream(&qstr) << "\n\nchannel: " << channel << " level: " << level << "v"; 933 | QTextStream(&qstr) << " is out of range.\n\nYou may need to adjust "; 934 | QTextStream(&qstr) << "the 2 volt offset potentiometer on this card.\n"; 935 | cout << "----------------------------------------------\n"; 936 | cout << qstr; 937 | cout << "----------------------------------------------\n"; 938 | emit errMessage(qstr); 939 | break; 940 | } 941 | 942 | // create a vector from the voltage levels 943 | aVoltageLevel = static_cast(level); 944 | voltageLevel.push_back( aVoltageLevel ); 945 | 946 | // create a vector from the computed voltage min 947 | aVoltageMin = gsl_stats_float_min( 948 | &(*Data)[0], 1, nPts); 949 | voltageMin.push_back( aVoltageMin ); 950 | 951 | // create a vector from the computed voltage max 952 | aVoltageMax = gsl_stats_float_max( 953 | &(*Data)[0], 1, nPts); 954 | voltageMax.push_back( aVoltageMax ); 955 | 956 | // create a vector from the computed voltage means 957 | aVoltageMean = gsl_stats_float_mean( 958 | &(*Data)[0], 1, nPts); 959 | voltageMean.push_back( aVoltageMean ); 960 | 961 | // create a vector from the computed voltage weights 962 | aVoltageWeight = gsl_stats_float_variance( 963 | &(*Data)[0], 1, nPts); 964 | aVoltageWeight = (aVoltageWeight == 0.0) ? 1.0 : (1.0 / aVoltageWeight); 965 | voltageWeight.push_back( aVoltageWeight ); 966 | 967 | std::cout << " aVoltageLevel: " << setprecision(7) << setw(12) << aVoltageLevel; 968 | std::cout << " | aVoltageMin: " << setprecision(7) << setw(12) << aVoltageMin; 969 | std::cout << " | aVoltageMax: " << setprecision(7) << setw(12) << aVoltageMax; 970 | std::cout << " | aVoltageMean: " << setprecision(7) << setw(12) << aVoltageMean; 971 | std::cout << " | aVoltageWeight: " << setprecision(7) << setw(12) << aVoltageWeight; 972 | std::cout << std::endl; 973 | std::cout << "calData[" << dsmId << "][" << devId << "][" << channel << "][" << level << "]" << std::endl; 974 | for (iiData = Data->begin(); iiData != Data->end(); iiData++) 975 | std::cout << setprecision(7) << setw(12) << *iiData; 976 | std::cout << std::endl; 977 | 978 | // detect measured values outside of desired level 979 | if ( (aVoltageMean < (aVoltageLevel - 1.0)) || 980 | (aVoltageMean > (aVoltageLevel + 1.0)) ) { 981 | 982 | if (detected[level]) continue; 983 | detected[level] = true; 984 | 985 | QTextStream cout(stdout, QIODevice::WriteOnly); 986 | 987 | QTextStream(&devErr) << "defective card? "; 988 | QTextStream(&devErr) << calFileName[dsmId][devId].c_str(); 989 | QTextStream(&devErr) << "\n\nchannel: " << channel << " level: " << level << "v\n"; 990 | QTextStream(&devErr) << "Internal uncalibrated voltage measures as "<< aVoltageMean << "v\n"; 991 | } 992 | } 993 | size_t nPts = voltageLevel.size(); 994 | double cov00, cov01, cov11, chisq; 995 | 996 | vector::iterator iVM = voltageMean.begin(); 997 | std::cout << "channel: " << channel << std::endl; 998 | for ( ; iVM != voltageMean.end(); iVM++ ) 999 | std::cout << "iVM: " << *iVM << std::endl; 1000 | std::cout << "voltageLevel.size(): " << nPts << std::endl; 1001 | 1002 | // compute weighted linear fit to the data 1003 | gsl_fit_wlinear (&voltageMean[0], 1, 1004 | &voltageWeight[0], 1, 1005 | &voltageLevel[0], 1, 1006 | nPts, 1007 | &c0[channel], &c1[channel], &cov00, &cov01, &cov11, &chisq); 1008 | 1009 | // store results for access by the Qt interface. 1010 | int gain = Gains[dsmId][devId][channel]; 1011 | int bplr = Bplrs[dsmId][devId][channel]; 1012 | resultCals[dsmId][devId][channel][gain][bplr].clear(); 1013 | resultCals[dsmId][devId][channel][gain][bplr].push_back(c0[channel]); 1014 | resultCals[dsmId][devId][channel][gain][bplr].push_back(c1[channel]); 1015 | } 1016 | // compute temperature mean 1017 | resultTemperature[dsmId][devId] = 1018 | gsl_stats_float_mean(&(temperatureData[dsmId][devId][0]), 1, 1019 | temperatureData[dsmId][devId].size()); 1020 | 1021 | // record results to the device's CalFile 1022 | ostringstream ostr; 1023 | ostr << setprecision(5); 1024 | ostr << std::endl; 1025 | ostr << "# auto_cal results..." << std::endl; 1026 | ostr << "# temperature: " << resultTemperature[dsmId][devId] << std::endl; 1027 | ostr << "# Date Gain Bipolar"; 1028 | for (uint ix = 0; ix < devNchannels[id(dsmId, devId)]; ix++) 1029 | ostr << " CH" << ix << "-off CH" << ix << "-slope"; 1030 | ostr << std::endl; 1031 | 1032 | // for each (gain, bplr) range 1033 | for (uint iGB=0; iGB<4; iGB++) { 1034 | 1035 | // find out if a channel was calibrated at this range 1036 | uint channel = 99; 1037 | for (uint ix = 0; ix < devNchannels[id(dsmId, devId)]; ix++) { 1038 | if ( ( Channels->find(ix) != Channels->end() ) && 1039 | ( Gains[dsmId][devId][ix] == GB[iGB].gain ) && 1040 | ( Bplrs[dsmId][devId][ix] == GB[iGB].bplr ) ) { 1041 | channel = ix; 1042 | break; 1043 | } 1044 | } 1045 | // display calibrations that were performed at this range 1046 | if ( channel != 99 ) { 1047 | ostr << n_u::UTime(timeStamp[dsmId][devId][channel]).format(true,"%Y %b %d %H:%M:%S"); 1048 | ostr << setw(6) << dec << GB[iGB].gain; 1049 | ostr << setw(9) << dec << GB[iGB].bplr; 1050 | 1051 | for (uint ix = 0; ix < devNchannels[id(dsmId, devId)]; ix++) { 1052 | if ( ( Channels->find(ix) != Channels->end() ) && 1053 | ( Gains[dsmId][devId][ix] == GB[iGB].gain ) && 1054 | ( Bplrs[dsmId][devId][ix] == GB[iGB].bplr ) ) 1055 | 1056 | ostr << " " << setw(9) << c0[ix] 1057 | << " " << setw(9) << c1[ix]; 1058 | else 1059 | ostr << " 0 1"; 1060 | } 1061 | ostr << std::endl; 1062 | } 1063 | } 1064 | // TODO provide the user the option to review the results before storing them 1065 | std::cout << "calFileName[" << dsmId << "][" << devId << "] = "; 1066 | std::cout << calFileName[dsmId][devId] << std::endl; 1067 | 1068 | calFileResults[dsmId][devId] = ostr.str(); 1069 | std::cout << calFileResults[dsmId][devId] << std::endl; 1070 | 1071 | // review the device error results 1072 | if (devErr.length()) 1073 | emit errMessage(devErr); 1074 | } 1075 | } 1076 | // show totals for Min and Max 1077 | std::cout << "voltageMin.size() = " << voltageMin.size() << std::endl; 1078 | std::cout << "voltageMax.size() = " << voltageMax.size() << std::endl; 1079 | double allVoltageMin = gsl_stats_float_min( 1080 | &(voltageMin[0]), 1, voltageMin.size()); 1081 | double allVoltageMax = gsl_stats_float_max( 1082 | &(voltageMax[0]), 1, voltageMax.size()); 1083 | std::cout << "allVoltageMin = " << allVoltageMin << std::endl; 1084 | std::cout << "allVoltageMax = " << allVoltageMax << std::endl; 1085 | 1086 | progress = maxProgress(); 1087 | } 1088 | 1089 | 1090 | void AutoCalClient::SaveAllCalFiles() 1091 | { 1092 | dsm_d_type::iterator iiDsm; 1093 | device_d_type::iterator iiDevice; 1094 | 1095 | // for each DSM 1096 | for (iiDsm = calData.begin(); 1097 | iiDsm != calData.end(); iiDsm++) { 1098 | 1099 | uint dsmId = iiDsm->first; 1100 | device_d_type* Devices = &(iiDsm->second); 1101 | 1102 | // for each device 1103 | for (iiDevice = Devices->begin(); 1104 | iiDevice != Devices->end(); iiDevice++) { 1105 | 1106 | uint devId = iiDevice->first; 1107 | 1108 | SaveCalFile(dsmId, devId); 1109 | } 1110 | } 1111 | } 1112 | 1113 | 1114 | void AutoCalClient::SaveCalFile(uint dsmId, uint devId) 1115 | { 1116 | size_t pos; 1117 | ostringstream ostr; 1118 | string aCalFile = calFilePath[dsmId][devId] + '/' + 1119 | calFileName[dsmId][devId]; 1120 | 1121 | // We are saving into an alternate directory instead of the applied cal 1122 | // directory. Bail if we don't understand save path. 1123 | if ((pos = aCalFile.find("/A2D/")) == string::npos) { 1124 | ostr << "Will not save to " << aCalFile; 1125 | QMessageBox::warning(0, "error", ostr.str().c_str()); 1126 | return; 1127 | } 1128 | 1129 | aCalFile.replace(pos, 5, "/auto_cal/"); 1130 | 1131 | if (calFileSaved[dsmId][devId]) { 1132 | ostr << "results already saved to: " << aCalFile; 1133 | std::cout << ostr.str() << std::endl; 1134 | QMessageBox::information(0, "notice", ostr.str().c_str()); 1135 | return; 1136 | } 1137 | 1138 | std::cout << "Appending results to: "; 1139 | std::cout << aCalFile << std::endl; 1140 | std::cout << calFileResults[dsmId][devId] << std::endl; 1141 | 1142 | int fd = open( aCalFile.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644); 1143 | if (fd == -1) { 1144 | ostr << "failed to save results to: " << aCalFile << std::endl; 1145 | ostr << strerror(errno); 1146 | std::cout << ostr.str() << std::endl; 1147 | QMessageBox::warning(0, "error", ostr.str().c_str()); 1148 | return; 1149 | } 1150 | write(fd, calFileResults[dsmId][devId].c_str(), 1151 | calFileResults[dsmId][devId].length()); 1152 | close(fd); 1153 | 1154 | /* comment out successful save dialog. cjw 11/20/2013 1155 | ostr << "saved results to: " << aCalFile; 1156 | std::cout << ostr.str() << std::endl; 1157 | QMessageBox::information(0, "notice", ostr.str().c_str()); 1158 | */ 1159 | calFileSaved[dsmId][devId] = true; 1160 | } 1161 | 1162 | 1163 | list AutoCalClient::GetVoltageLevels() 1164 | { 1165 | return voltageLevels["XX"]; 1166 | } 1167 | 1168 | 1169 | list AutoCalClient::GetVoltageLevels(uint dsmId, uint devId, uint chn) 1170 | { 1171 | if ( VarNames[dsmId][devId][chn] == "" ) 1172 | return voltageLevels["--"]; 1173 | 1174 | int gain = Gains[dsmId][devId][chn]; 1175 | int bplr = Bplrs[dsmId][devId][chn]; 1176 | 1177 | ostringstream gb; 1178 | if (cardType[id(dsmId, devId)] == "gpDAQ" || 1179 | cardType[id(dsmId, devId)] == "dmmat") 1180 | gb << cardType[id(dsmId, devId)]; 1181 | else 1182 | gb << gain << (bplr ? "T" : "F"); 1183 | 1184 | return voltageLevels[gb.str()]; 1185 | } 1186 | 1187 | 1188 | string AutoCalClient::GetVarName(uint dsmId, uint devId, uint chn) 1189 | { 1190 | if ( VarNames[dsmId][devId][chn] == "" ) 1191 | return "---"; 1192 | 1193 | QStrBuf.str(""); QStrBuf << VarNames[dsmId][devId][chn]; 1194 | return QStrBuf.str(); 1195 | } 1196 | 1197 | float AutoCalClient::GetVoltageData(uint dsmId, uint devId, uint chn) 1198 | { 1199 | float voltage = testData[dsmId][devId][chn]; 1200 | // IFSR = 1: volts = 5 * (codes / 2^(20-1) - 1) = -5 + 5 / 524288 * codes 1201 | // IFSR = 0: volts = 10 * (codes / 2^(20-1) - 1) = -10 + 10 / 524288 * codes 1202 | if (cardType[id(dsmId, devId)] == "gpDAQ") 1203 | { 1204 | if (Gains[dsmId][devId][chn] == 1) 1205 | voltage = -10.0 + 10.0 / 524288 * voltage; 1206 | if (Gains[dsmId][devId][chn] == 2) 1207 | voltage = -5.0 + 5.0 / 524288 * voltage; 1208 | } 1209 | 1210 | return voltage; 1211 | } 1212 | 1213 | string AutoCalClient::GetOldTimeStamp(uint dsmId, uint devId, uint chn) 1214 | { 1215 | int gain = Gains[dsmId][devId][chn]; 1216 | int bplr = Bplrs[dsmId][devId][chn]; 1217 | 1218 | if (calFileTime[dsmId][devId][gain][bplr] == 0) 1219 | return "---- --- -- --:--:--"; 1220 | 1221 | return n_u::UTime(calFileTime[dsmId][devId][gain][bplr]).format(true,"%Y %b %d %H:%M:%S"); 1222 | } 1223 | 1224 | 1225 | string AutoCalClient::GetNewTimeStamp(uint dsmId, uint devId, uint chn) 1226 | { 1227 | if (timeStamp[dsmId][devId][chn] == 0) 1228 | return "---- --- -- --:--:--"; 1229 | 1230 | return n_u::UTime(timeStamp[dsmId][devId][chn]).format(true,"%Y %b %d %H:%M:%S"); 1231 | } 1232 | 1233 | 1234 | float AutoCalClient::GetOldTemperature(uint dsmId, uint devId, uint chn) 1235 | { 1236 | return floatNAN; 1237 | } 1238 | 1239 | 1240 | float AutoCalClient::GetNewTemperature(uint dsmId, uint devId, uint chn) 1241 | { 1242 | return resultTemperature[dsmId][devId]; 1243 | } 1244 | 1245 | 1246 | vector AutoCalClient::GetOldCals(uint dsmId, uint devId, uint chn) 1247 | { 1248 | int gain = Gains[dsmId][devId][chn]; 1249 | int bplr = Bplrs[dsmId][devId][chn]; 1250 | 1251 | return calFileCals[dsmId][devId][chn][gain][bplr]; 1252 | } 1253 | 1254 | 1255 | vector AutoCalClient::GetNewCals(uint dsmId, uint devId, uint chn) 1256 | { 1257 | int gain = Gains[dsmId][devId][chn]; 1258 | int bplr = Bplrs[dsmId][devId][chn]; 1259 | 1260 | return resultCals[dsmId][devId][chn][gain][bplr]; 1261 | } 1262 | 1263 | 1264 | dsm_sample_id_t AutoCalClient::id(unsigned int dsmId, unsigned int devId) 1265 | { 1266 | dsm_sample_id_t id = 0; 1267 | id = SET_DSM_ID(id, dsmId); 1268 | id = SET_SPS_ID(id, devId); 1269 | return id; 1270 | } 1271 | --------------------------------------------------------------------------------