bool MainWindow::qt_invoke( int _id, QUObject* _o ) { switch ( _id - staticMetaObject()->slotOffset() ) { case 0: slotPrint(); break; case 1: slotClearPsth(); break; case 2: slotTriggerPsth(); break; case 3: slotSetChannel((double)static_QUType_double.get(_o+1)); break; case 4: slotSetPsthLength((double)static_QUType_double.get(_o+1)); break; case 5: slotSetPsthBinw((double)static_QUType_double.get(_o+1)); break; case 6: slotSetSpikeThres((double)static_QUType_double.get(_o+1)); break; case 7: slotSavePsth(); break; case 8: slotSetNumTrials((double)static_QUType_double.get(_o+1)); break; case 9: slotStartPsthRec(); break; case 10: slotSoundToggle(); break; case 11: slotAveragePsth(); break; default: return QWidget::qt_invoke( _id, _o ); } return TRUE; }
MainWindow::MainWindow( QWidget *parent ) : QWidget(parent), adChannel(0), psthLength(1000), psthBinw(20), spikeThres(1), psthOn(0), spikeDetected(false), time(0), linearAverage(0) { // initialize comedi const char *filename = "/dev/comedi0"; /* open the device */ if( (dev = comedi_open(filename)) == 0 ) { comedi_perror(filename); exit(1); } // do not produce NAN for out of range behaviour comedi_set_global_oor_behavior(COMEDI_OOR_NUMBER); maxdata = comedi_get_maxdata(dev, COMEDI_SUB_DEVICE, 0); crange = comedi_get_range(dev,COMEDI_SUB_DEVICE,0,0); numChannels = comedi_get_n_channels(dev, COMEDI_SUB_DEVICE); chanlist = new unsigned[numChannels]; /* Set up channel list */ for( int i=0; i<numChannels; i++ ) chanlist[i] = CR_PACK(i, COMEDI_RANGE_ID, AREF_GROUND); int ret = comedi_get_cmd_generic_timed( dev, COMEDI_SUB_DEVICE, &comediCommand, numChannels, (int)(1e9/(SAMPLING_RATE)) ); if(ret < 0) { printf("comedi_get_cmd_generic_timed failed\n"); exit(-1); } /* Modify parts of the command */ comediCommand.chanlist = chanlist; comediCommand.stop_src = TRIG_NONE; comediCommand.stop_arg = 0; /* comedi_command_test() tests a command to see if the * trigger sources and arguments are valid for the subdevice. * If a trigger source is invalid, it will be logically ANDed * with valid values (trigger sources are actually bitmasks), * which may or may not result in a valid trigger source. * If an argument is invalid, it will be adjusted to the * nearest valid value. In this way, for many commands, you * can test it multiple times until it passes. Typically, * if you can't get a valid command in two tests, the original * command wasn't specified very well. */ ret = comedi_command_test(dev, &comediCommand); if(ret < 0) { comedi_perror("comedi_command_test"); exit(-1); } fprintf(stderr, "first test returned %d\n", ret); ret = comedi_command_test(dev, &comediCommand); if(ret < 0) { comedi_perror("comedi_command_test"); exit(-1); } fprintf(stderr, "second test returned %d\n", ret); if(ret != 0) { fprintf(stderr,"Error preparing command\n"); exit(-1); } // the timing is done channel by channel // this means that the actual sampling rate is divided by // number of channels if ((comediCommand.convert_src == TRIG_TIMER)&&(comediCommand.convert_arg)) { sampling_rate=(((double)1E9 / comediCommand.convert_arg)/numChannels); } // the timing is done scan by scan (all channels at once) // the sampling rate is equivalent of the scan_begin_arg if ((comediCommand.scan_begin_src == TRIG_TIMER)&&(comediCommand.scan_begin_arg)) { sampling_rate=(double)1E9 / comediCommand.scan_begin_arg; } // 50Hz or 60Hz mains notch filter iirnotch = new Iir::Butterworth::BandStop<IIRORDER>; assert( iirnotch != NULL ); iirnotch->setup (IIRORDER, sampling_rate, NOTCH_F, NOTCH_F/10.0); /* start the command */ ret = comedi_command(dev, &comediCommand); if(ret < 0) { comedi_perror("comedi_command"); exit(1); } int subdev_flags = comedi_get_subdevice_flags(dev, COMEDI_SUB_DEVICE); if( (sigmaBoard = subdev_flags & SDF_LSAMPL) ) readSize = sizeof(lsampl_t) * numChannels; else readSize = sizeof(sampl_t) * numChannels; // Initialize data for plots for(int i=0; i<MAX_PSTH_LENGTH; i++) { xData[i] = i; // time axis yData[i] = 0; timeData[i] = double(i)*psthBinw; // psth time axis spikeCountData[i] = 0; psthData[i] = 0; } // the gui, straight forward QT/Qwt resize(640,420); QHBoxLayout *mainLayout = new QHBoxLayout( this ); QVBoxLayout *controlLayout = new QVBoxLayout; mainLayout->addLayout(controlLayout); QVBoxLayout *plotLayout = new QVBoxLayout; plotLayout->addStrut(400); mainLayout->addLayout(plotLayout); // two plots RawDataPlot = new DataPlot(xData, yData, psthLength, crange->max, crange->min, this); plotLayout->addWidget(RawDataPlot); RawDataPlot->show(); plotLayout->addSpacing(20); MyPsthPlot = new PsthPlot(timeData, psthData, psthLength/psthBinw, this); plotLayout->addWidget(MyPsthPlot); MyPsthPlot->show(); /*---- Buttons ----*/ // AD group QGroupBox *ADcounterGroup = new QGroupBox( "A/D Channel", this ); QVBoxLayout *ADcounterLayout = new QVBoxLayout; ADcounterGroup->setLayout(ADcounterLayout); ADcounterGroup->setAlignment(Qt::AlignJustify); ADcounterGroup->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); controlLayout->addWidget( ADcounterGroup ); QwtCounter *cntChannel = new QwtCounter(ADcounterGroup); cntChannel->setRange(0, numChannels-1, 1); cntChannel->setValue(adChannel); ADcounterLayout->addWidget(cntChannel); connect(cntChannel, SIGNAL(valueChanged(double)), SLOT(slotSetChannel(double))); filter50HzCheckBox = new QCheckBox( "50Hz filter" ); filter50HzCheckBox->setEnabled( true ); ADcounterLayout->addWidget(filter50HzCheckBox); // psth functions QGroupBox *PSTHfunGroup = new QGroupBox( "Actions", this ); QVBoxLayout *PSTHfunLayout = new QVBoxLayout; PSTHfunGroup->setLayout(PSTHfunLayout); PSTHfunGroup->setAlignment(Qt::AlignJustify); PSTHfunGroup->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); controlLayout->addWidget( PSTHfunGroup ); averagePsth = new QComboBox(PSTHfunGroup); averagePsth->addItem(tr("PSTH")); averagePsth->addItem(tr("VEP")); PSTHfunLayout->addWidget(averagePsth); connect( averagePsth, SIGNAL(currentIndexChanged(int)), SLOT(slotAveragePsth(int)) ); triggerPsth = new QPushButton(PSTHfunGroup); triggerPsth->setText("PSTH on"); triggerPsth->setCheckable(true); PSTHfunLayout->addWidget(triggerPsth); connect(triggerPsth, SIGNAL(clicked()), SLOT(slotTriggerPsth())); QPushButton *clearPsth = new QPushButton(PSTHfunGroup); clearPsth->setText("clear data"); PSTHfunLayout->addWidget(clearPsth); connect(clearPsth, SIGNAL(clicked()), SLOT(slotClearPsth())); QPushButton *savePsth = new QPushButton(PSTHfunGroup); savePsth->setText("save data"); PSTHfunLayout->addWidget(savePsth); connect(savePsth, SIGNAL(clicked()), SLOT(slotSavePsth())); // psth params QGroupBox *PSTHcounterGroup = new QGroupBox( "Parameters", this ); QVBoxLayout *PSTHcounterLayout = new QVBoxLayout; PSTHcounterGroup->setLayout(PSTHcounterLayout); PSTHcounterGroup->setAlignment(Qt::AlignJustify); PSTHcounterGroup->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); controlLayout->addWidget( PSTHcounterGroup ); QLabel *psthLengthLabel = new QLabel("Sweep length", PSTHcounterGroup); PSTHcounterLayout->addWidget(psthLengthLabel); QwtCounter *cntSLength = new QwtCounter(PSTHcounterGroup); cntSLength->setNumButtons(2); cntSLength->setIncSteps(QwtCounter::Button1, 10); cntSLength->setIncSteps(QwtCounter::Button2, 100); cntSLength->setRange(1, MAX_PSTH_LENGTH, 1); cntSLength->setValue(psthLength); PSTHcounterLayout->addWidget(cntSLength); connect(cntSLength, SIGNAL(valueChanged(double)), SLOT(slotSetPsthLength(double))); QLabel *binwidthLabel = new QLabel("Binwidth", PSTHcounterGroup); PSTHcounterLayout->addWidget(binwidthLabel); cntBinw = new QwtCounter(PSTHcounterGroup); cntBinw->setNumButtons(2); cntBinw->setIncSteps(QwtCounter::Button1, 1); cntBinw->setIncSteps(QwtCounter::Button2, 10); cntBinw->setRange(1, 100, 1); cntBinw->setValue(psthBinw); PSTHcounterLayout->addWidget(cntBinw); connect(cntBinw, SIGNAL(valueChanged(double)), SLOT(slotSetPsthBinw(double))); QLabel *thresholdLabel = new QLabel("Spike Threshold", PSTHcounterGroup); PSTHcounterLayout->addWidget(thresholdLabel); editSpikeT = new QTextEdit("0"); QFont editFont("Courier",14); QFontMetrics editMetrics(editFont); editSpikeT->setMaximumHeight ( editMetrics.height()*1.2 ); editSpikeT->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); editSpikeT->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); editSpikeT->setFont(editFont); PSTHcounterLayout->addWidget(editSpikeT); connect(editSpikeT, SIGNAL(textChanged()), SLOT(slotSetSpikeThres())); thresholdMarker = new QwtPlotMarker(); thresholdMarker->setValue(0,0); thresholdMarker->attach(RawDataPlot); thresholdMarker->setLineStyle(QwtPlotMarker::HLine); // Generate timer event every 50ms (void)startTimer(50); }