#include "GUI.h" //--------------------------------------------------------------------- //************************ All functions **************************** //------------------------------------------------------------------- // +++ Open file dialog +++ void ddd::FileDialog(void) { QString Filename = QFileDialog::getOpenFileName(this, "Open raw file", INITIAL_DIRECTORY, "Raw data files (*.raw);; All files (*)"); if (Filename != NULL) { FilenameBox->setText(Filename); OpenDatafile(); } } // +++ Open selected file and read run header +++ void ddd::OpenDatafile() { if(Socket->state() == QAbstractSocket::ConnectedState) { // do not execute if socket is open MakeConnection(); } CloseDatafile(); // Close previous file if open // Write run header to temporary file ftruncate(fileno(Tmpfile),0); rewind(Tmpfile); switch (RD->OpenDataFile(FilenameBox->text().toAscii().data(), Tmpfile)) { case CTX_FOPEN: QMessageBox::warning(this, "ddd Message","Could not open file.",QMessageBox::Ok); return; case CTX_RHEADER: QMessageBox::warning(this, "ddd Message","Could not read run header.",QMessageBox::Ok); return; case CTX_BSTRUCT: QMessageBox::warning(this, "ddd Message","Could not read board structures.",QMessageBox::Ok); return; default: break; } RunHeader *R = RD->RHeader; if (R->MagicNum == MAGICNUM_OPEN) { QMessageBox::warning(this, "ddd Message","Magic number in run header indicates that the file has not been closed properly.",QMessageBox::Ok); } if (R->MagicNum == MAGICNUM_ERROR) { QMessageBox::warning(this, "ddd Message","Magic number in run header indicates that an error occurred while writing the file.",QMessageBox::Ok); } rewind(Tmpfile); QTextStream in(Tmpfile); QString text = in.readAll(); RunHeaderDisplay->setPlainText(text); // Enable spin boxes, set ranges and display first event EventNo->setEnabled(true); ChannelNo->setEnabled(true); ChipNo->setEnabled(true); BoardNo->setEnabled(true); PixelID->setEnabled(true); M0Start->setEnabled(true); M0Stop->setEnabled(true); PhysPipeAction->setEnabled(true); EventNo->setRange(1, R->Events); M0Display->setEnabled(true); M0Start->setRange(0, R->Samples-1); M0Stop->setRange(0, R->Samples-1); ChannelNo->setRange(0, R->NChannels-1); ChipNo->setRange(0, R->NChips-1); BoardNo->setRange(0, R->NBoards-1); DisplayEvent(); } // +++ Close data file file if open, delete event header and disable spin boxes and displays +++ void ddd::CloseDatafile() { if(RD->CloseDataFile()==CTX_OK) { EventNo->setEnabled(false); ChannelNo->setEnabled(false); ChipNo->setEnabled(false); BoardNo->setEnabled(false); PixelID->setEnabled(false); M0Start->setEnabled(false); M0Stop->setEnabled(false); PhysPipeAction->setEnabled(false); RunHeaderDisplay->clear(); EventHeaderDisplay->clear(); Signal->hide(); } } // +++ Read event header and display event (only called if Datafile is open) +++ void ddd::DisplayEvent(int) { PixelID->setText(PixMap->DRS_to_Pixel(BoardNo->value(), ChipNo->value(), ChannelNo->value()).c_str()); // Translate to pixel ID if(Socket->state() == QAbstractSocket::ConnectedState) return; // do not execute if socket is open if (PhysPipeAction->isChecked()) { Graph->setAxisTitle(QwtPlot::xBottom, "Time from start of physical pipeline (ns)"); } else { Graph->setAxisTitle(QwtPlot::xBottom, "Time from trigger (ns)"); } // Read event ftruncate(fileno(Tmpfile),0); rewind(Tmpfile); if (RD->ReadEvent(EventNo->value(), Tmpfile) != CTX_OK) { QMessageBox::warning(this, "ddd Warning","Could not read event.",QMessageBox::Ok); EventHeaderDisplay->clear(); return; } RunHeader *R = RD->RHeader; // Print event header and trigger cell information from event data rewind(Tmpfile); QTextStream in(Tmpfile); QString text = in.readAll(); text.append("\nTrigger cells: "); for (unsigned int i=0; iNBoards*R->NChips; i++) { QString a; text.append(a.sprintf("%d ", *((int *)RD->Data + i))); } EventHeaderDisplay->setPlainText(text); // Case data in double format required by qwt library double *x = new double [R->Samples]; double *y = new double [R->Samples]; for (unsigned int i=0; iSamples; i++) { x[i] = (double) (i/RD->BStruct[BoardNo->value()].NomFreq); if (PhysPipeAction->isChecked()) { y[(i + *((int *) RD->Data + BoardNo->value()*R->NChips+ChipNo->value()))%1024] = (double) *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) + BoardNo->value()*R->NChips*R->NChannels*R->Samples + ChipNo->value()*R->NChannels*R->Samples + ChannelNo->value()*RD->RHeader->Samples + i) * RD->BStruct[BoardNo->value()].ScaleFactor; } else { y[i] = (double) *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) + BoardNo->value()*R->NChips*R->NChannels*R->Samples + ChipNo->value()*R->NChannels*R->Samples + ChannelNo->value()*R->Samples + i) * RD->BStruct[BoardNo->value()].ScaleFactor; } } Signal->setData(x, y, (int) R->Samples); Signal->show(); Zoomer->setZoomBase(Signal->boundingRect()); delete[] x; delete[] y; // ************************************ // Get data for M0 display (event based) // ************************************ if (!(M0Display->isEnabled())) return; double z[6][6] = {{0}};//36 pixels bool IDerror = false; //only interested in M0 data using DRS2 //if (RD->RHeader->NChannels == 9) IDerror = true; //only interested in M0 data using DRS4 if (RD->RHeader->NChannels == 10) IDerror = true; for(unsigned int i=0; iRHeader->NBoards; i++) {//board loop for(unsigned int j=0; jRHeader->NChips; j++) {//chip loop for(unsigned int k=0; kRHeader->NChannels; k++) {//channel loop //for DRS2 data (requires the correct pixelmap!!! //if( ( (i==0 || i==1) && (j<=1) && (k<=7) ) || ( (i==2) && (j==0) && (k<=3) ) ) { //for DRS4 data (requires the correct pixelmap!!! if( ( (i==0 || i==1) && (j<=3) && (k==0 || k==2 || k==4 || k==6) ) || ( (i==2) && (j==0) && (k==0 || k==2 || k==4 || k==6) ) ) { //get module, superpixel and pixel number from pixel name std::string pixelname = PixMap->DRS_to_Pixel(i,j,k); if (pixelname == "") { IDerror = true; continue; } char pixelname_copy[256]; memset(pixelname_copy,'\0',256); pixelname.copy(pixelname_copy, 256); char delim[] = "-"; char *buffer = NULL; int module = -1; int superpixel = -1; int pixel = -1; buffer = strtok(pixelname_copy, delim); module = atoi(buffer); buffer = strtok(NULL, delim); superpixel = atoi(buffer); buffer = strtok(NULL, delim); pixel = atoi(buffer); //usual M0 mapping //int binx = 5-(int((superpixel-1)/3)*2)-(int((pixel%4)/2)); //int biny = 5-(((superpixel-1)%3)*2)-(int((pixel-1)/2)); //M0 upside down int binx = 5-(5-(int((superpixel-1)/3)*2)-(int((pixel%4)/2))); int biny = 5-(5-(((superpixel-1)%3)*2)-(int((pixel-1)/2))); //search maximum sample amplitude within user specified window //start bin is always smaller than stop bin (taken care of by updated ranges) int StartBin = (int)(M0Start->value()); int StopBin = (int)(M0Stop->value()); for(int l=StartBin; l<=StopBin; l++){ float sample = *((short *) (RD->Data + RD->RHeader->NBoards*RD->RHeader->NChips*sizeof(int)) + i*RD->RHeader->NChips*RD->RHeader->NChannels*RD->RHeader->Samples+ j*RD->RHeader->NChannels*RD->RHeader->Samples+ k*RD->RHeader->Samples+ l)*RD->BStruct[i].ScaleFactor; if (fabs(sample) > z[binx][biny]) { z[binx][biny]=fabs(sample); } }//sample loop }//only M0 data }//channel loop }//chip loop }//board loop if(IDerror){ QMessageBox::warning(this, "ddd Message","Sorry! The M0 display is not available for this data file because of a pixel ID mismatch.",QMessageBox::Ok); if(M0Window->isVisible()) M0Window->hide(); M0Display->setEnabled(false); return; } //fill data to M0 display (event based) Signal2D->setData(SpectrogramDataM0(z)); Graph2D->axisWidget(QwtPlot::yRight)->setColorMap(Signal2D->data().range(),Signal2D->colorMap()); Graph2D->setAxisScale(QwtPlot::yRight,Signal2D->data().range().minValue(),Signal2D->data().range().maxValue() ); Graph2D->replot(); //update ranges for start and stop bin to avoid startbin > stopbin M0Start->setRange(0, M0Stop->value()); M0Stop->setRange(M0Start->value(),(RD->RHeader->Samples)-1); } // +++ Open sub window handling the socket interface +++ void ddd::OpenSocketWindow() { if(SocketWindow->isVisible()) SocketWindow->hide(); else SocketWindow->show(); } // +++ Open sub window for M0 Display +++ void ddd::OpenM0Window() { if(M0Window->isVisible()) M0Window->hide(); else M0Window->show(); } // +++ Acquire data through socket (acquire botton only available if socket exists) +++ void ddd::GetSignalFromSocket() { char Command[MAX_COM_SIZE]; GetButton->setEnabled(false); WaitForData = true; sprintf(Command, "read %d %d %d restart", BoardNo->value(), ChipNo->value(), ChannelNo->value()); Socket->write(Command); } // Quit application when clicking close button on window void ddd::closeEvent(QCloseEvent *) { qApp->quit(); } // +++ Connecting or disconnecting from client +++ void ddd::MakeConnection() { if(Socket->state() == QAbstractSocket::ConnectedState) { ManualDisconnect = true; Socket->disconnectFromHost(); } else { if (RD->IsFileOpen() && QMessageBox::question(this, "ddd Request","Connecting will close current data file. Proceed?", QMessageBox::No, QMessageBox::Yes) != QMessageBox::Yes) return; PhysPipeAction->setChecked(false); Socket->connectToHost(IPAddress->text(),Port->value()); Connect->setEnabled(false); // While waiting for connection, button not available Socket->waitForConnected(SOCKET_TIMEOUT); Connect->setEnabled(true); if(Socket->state() != QAbstractSocket::ConnectedState) QMessageBox::warning(this, "ddd Message","Could not connect to host.",QMessageBox::Ok); else { Connect->setText("Disconnect"); ConnectAction->setText("Disconnect"); Port->setEnabled(false); IPAddress->setEnabled(false); Command->setEnabled(true); GetButton->setEnabled(true); ManualDisconnect = false; FilenameBox->clear(); CloseDatafile(); M0Window->hide(); M0Display->setEnabled(false); ChannelNo->setEnabled(true); ChipNo->setEnabled(true); BoardNo->setEnabled(true); PixelID->setEnabled(true); ChannelNo->setRange(0, 65535); ChipNo->setRange(0, 65535); BoardNo->setRange(0, 65535); TabWidget->setTabEnabled(1,false); TabWidget->setTabEnabled(2,false); RunHeaderDisplay->clear(); EventHeaderDisplay->clear(); Signal->hide(); } } } // +++ Send command to socket (command button available only if socket existing) +++ void ddd::SendToSocket() { Socket->write(Command->text().toAscii()); Command->clear(); } // +++ Read data from socket and display +++ void ddd::ReadFromSocket() { // Check if channel data expected and error message arrived QByteArray Data = Socket->readAll(); if (WaitForData && Data.contains("Error")) { WaitForData = false; GetButton->setEnabled(true); QMessageBox::warning(this, "ddd Message","Could not read waveform data from socket.",QMessageBox::Ok); return; } // Check if channel data were transmitted, if yes and waiting for data, extract and plot them SocketOutput->insertPlainText(Data); QString Text = SocketOutput->document()->toPlainText().trimmed(); if (WaitForData && Text.endsWith(QLatin1String("==END=="))) { // Extract text between ==START== and ==END== QByteArray Data = Text.mid(Text.lastIndexOf("==START==")+9, Text.length() - Text.lastIndexOf("==START==")-16).toAscii(); char *NextNumber = strtok(Data.data()," "); // Number of entries that follow int Count=0, NumberOfEntries = atoi(NextNumber); double *x = new double [NumberOfEntries]; double *y = new double [NumberOfEntries]; // Convert all entries (separated by a whitespace) to numbers while((NextNumber=strtok(NULL, " "))!=NULL && Count2) { Signal->setData(x+2, y+2, NumberOfEntries-2); // Copies data, arrays can be deleted afterwards Signal->show(); Zoomer->setZoomBase(Signal->boundingRect()); Graph->setAxisTitle(QwtPlot::xBottom, "Time from trigger (ns)"); } delete[] x; delete[] y; if(ContinuousBox->isChecked()) { usleep(100000); // Wait to limit maximum update rate GetSignalFromSocket(); } else { WaitForData = false; GetButton->setEnabled(true); } } } // +++ Reset graph axes to autoscale when fully unzoomed +++ void ddd::HandleZoom(const QwtDoubleRect &) { if(Zoomer->zoomRectIndex() == 0) { Graph->setAxisAutoScale(QwtPlot::xBottom); Graph->setAxisAutoScale(QwtPlot::yLeft); } } // +++ Disconnect from socket +++ void ddd::GotDisconnected() { Connect->setText("Connect"); ConnectAction->setText("Connect"); Port->setEnabled(true); IPAddress->setEnabled(true); Command->setEnabled(false); GetButton->setEnabled(false); ChannelNo->setEnabled(false); ChipNo->setEnabled(false); BoardNo->setEnabled(false); PixelID->setEnabled(false); Signal->hide(); TabWidget->setTabEnabled(1, true); TabWidget->setTabEnabled(2, true); SocketOutput->clear(); if(!ManualDisconnect) QMessageBox::warning(this, "ddd Message","Socket disconnected, maybe host gone.",QMessageBox::Ok); } // +++ Translate pixel ID +++ void ddd::TranslatePixelID() { int Board = PixMap->Pixel_to_DRSboard(PixelID->text().toStdString()); int Chip = PixMap->Pixel_to_DRSchip(PixelID->text().toStdString()); int Channel = PixMap->Pixel_to_DRSchannel(PixelID->text().toStdString()); if(Board >= BoardNo->minimum() && Board <= BoardNo->maximum() && Chip >= ChipNo->minimum() && Chip <= ChipNo->maximum() && Channel>= ChannelNo->minimum() && Channel <= ChannelNo->maximum()) { BoardNo->setValue(Board); ChipNo->setValue(Chip); ChannelNo->setValue(Channel); } else if(Board==999999999) QMessageBox::warning(this, "ddd Message","Pixel ID unknown.",QMessageBox::Ok); else QMessageBox::warning(this, "ddd Message","Pixel ID out of current range.",QMessageBox::Ok); } //------------------------------------------------------------------ //**************************** All menus *************************** //------------------------------------------------------------------ void ddd::MenuSave() { QString Filename = QFileDialog::getSaveFileName(this, "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)"); if (Filename.length()>0) { QPixmap Pixmap = QPixmap::grabWidget(Graph); if(!Pixmap.save(Filename)) { QMessageBox::warning(this, "ddd Message","Could not write image file.",QMessageBox::Ok); remove(Filename.toAscii().data()); } } } void ddd::MenuPrint() { QPrinter *Printer = new QPrinter; QPrintDialog *PrintDialog = new QPrintDialog(Printer, this); if (PrintDialog->exec() == QDialog::Accepted) { QPainter Painter(Printer); QPixmap Pixmap = QPixmap::grabWidget(Graph); Painter.drawPixmap(0, 0, Pixmap); } delete Printer; delete PrintDialog; } void ddd::MenuSaveASCII() { QString Filename = QFileDialog::getSaveFileName(this, "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)"); if (Filename.length()>0) { QFile File(Filename); if (File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) { QTextStream Stream(&File); for (int i=0; idataSize(); i++) { Stream << Signal->y(i) << endl; } } else { QMessageBox::warning(this, "ddd Message","Could not write data to file.",QMessageBox::Ok); } } } void ddd::MenuHelp() { QMessageBox Message; Message.setText("The DRS Data Display program can be used for two purposes:\n\n" "1. Reading and displaying the content of a raw data file written by the drsdaq program\n" "2. Acquiring and displaying online data from a running drsdaq program via the socket interface\n\n" "With an established socket connection, displaying of raw data files is disabled.\n\n" "Navigation in signal display: Left mouse button to zoom, right to unzoom fully, middle to unzoom one level. Left mouse button plus shift key to pan.\n" "When unzoomed, the axes are rescaled automatically."); Message.setWindowTitle("ddd Help"); Message.exec(); } void ddd::MenuAbout() { QMessageBox::about(this, "ddd About","DRS Data Display\n\n" "Written by Oliver Grimm, IPP, ETH Zurich\n" "Event display by Quirin Weitzel.\n\n" "This version compiled "__DATE__".\n\n" "Graphical user interface implemented with Qt.\n" "Bug reports to oliver.grimm@phys.ethz.ch."); }