#include #include #include #include #include #include #include using namespace std; #include #include #include #include #include #include #include #include #include #include #include #include #include string SCOPE_URL = "http://169.254.5.220"; int SOCKET_PORT = 4000; int NCHANNELS = 1; int MAX_EVENTS = 10; int TIMEOUT_SECS = 300; float TIMING_THRESHOLD_FRAC = 0.10; // time is at bin that is 10% of signal amplitude int REBIN_FACTOR = 1; // set to 1 for no rebinning bool SAVE_WAVEFORMS = false; string ROOT_FNAME = "ScopeDAQ.root"; bool QUIT = false; int NR_PT = 0; float XINCR = 0; float XZERO = 0; float YMULT = 0; float YZERO = 0; enum SCOPE_MODEL_t{ UNKNOWN, TD3034, DPO4034B, DPO4104, NSCOPE_MODELS }; SCOPE_MODEL_t SCOPE_MODEL = DPO4034B; string SCOPE_MODEL_ID = "UNKNOWN"; // Data structure used to accumulate data in single buffer // while downloading waveform typedef struct{ char *buff; int buff_size; int Nbytes; }DataBuffer_t; // Parameters used to convert data to physical units. // This is parsed from the header info returned from // the scope (which may differ slightly between models). class ConversionParms{ public: unsigned int NR_PT; int BYT_NR; double YOFF; double YMULT; double XINCR; double XZERO; int Ch; double V_per_div; double s_per_div; }; // Function declarations void GetScopeModel(string scopeURL); void PrintAcquisitionParameters(void); void Acquire(string scopeURL, int Nchannels, unsigned int Nevents, unsigned int timeout_secs); void AcquireStart(string scopeURL); void AcquireStop(string scopeURL); bool AcquireGetState(string scopeURL); string TriggerGetState(string scopeURL); unsigned int SendCommand(string scopeURL, string command, char *buff=NULL, unsigned int buff_size=0); TH1F* GetWaveform(string scopeURL, int channel); size_t ReceiveData( char *ptr, size_t size, size_t nmemb, DataBuffer_t *databuffer); TH1F* TektronixInternalToRootHist(const char *buff, int buff_size, string hname); void ParseHeader3034(string header, class ConversionParms &parms); void ParseHeader4104(string header, class ConversionParms &parms); void ParseCommandLineArguments(int narg, char *argv[]); void Usage(void); #define _DBG_ cerr<<__FILE__<<":"<<__LINE__<<" " #define _DBG__ cerr<<__FILE__<<":"<<__LINE__<Write(); f->Close(); delete f; return 0; } //------------------------- // GetScopeModel //------------------------- void GetScopeModel(string scopeURL) { char buff[8192]; bzero(buff, 8192); unsigned int Nbytes = SendCommand(scopeURL, ":ID?", buff, 8192); if(Nbytes<=0)return; SCOPE_MODEL_ID = string(buff); cout << "-------------------------------------------------------------------------" << endl; if(SCOPE_MODEL_ID.find("TEK/TDS 3034") != string::npos){ SCOPE_MODEL = TD3034; }else if(SCOPE_MODEL_ID.find("DPO4034B") != string::npos){ SCOPE_MODEL = DPO4034B; cout << "Found DPO4034B" << endl; cout << " This will try reading via direct socket connection. Only the last part" << endl; cout << "of the URL will be used (i.e. the \"http://\" part will be chopped off)." << endl; cout << "Please use the \"Utility\" button at the bottom of the scope to make sure" << endl; cout << "the port number for the socet is set to " << SOCKET_PORT << "." << endl; cout << "IMPORTANT: Make sure the \"Protocol\" under the Socket Server submenu is" << endl; cout << "set to \"Terminal\"." << endl; }else{ cout << "Couldn't find scope model in \"" << buff << "\"" << endl; } cout << "-------------------------------------------------------------------------" << endl; } //------------------------- // PrintAcquisitionParameters //------------------------- void PrintAcquisitionParameters(void) { cout< svals; string item; stringstream ss(buff); while(getline(ss, item, ';')) svals.push_back(item); if(svals.size() < 16){ cerr << "ERROR: number of items in waveform pramble <16 (=" << svals.size() << ")" << endl; cerr << buff << endl; return; } NR_PT = atoi(svals[6].c_str()); XINCR = atof(svals[9].c_str()); XZERO = atof(svals[10].c_str()); YMULT = atof(svals[13].c_str()); YZERO = atof(svals[14].c_str());; if(svals[12] == "\"V\"") YMULT*=1000.0; // make values in mV // Create a ROOT tree to hold the integrated, pedestal subtracted values int Nvals = 1 + NCHANNELS*3; float *vals = new float[Nvals]; // 1=eventno *3=(integral+pedestal+time) for(int ii=0; iiBranch("T", vals, varexp.str().c_str()); // Complete waveforms are stored in 2D histos. The dimensions are determined // by the data format returned by the scope and so the 2-D histos can't be // created until the first waveform is read in. We set the pointers to NULL // in order to flag that the histo hasn't been created yet. TH2F *h2[10]; for(int i=1; i<=Nchannels; i++){ h2[i] = NULL; } cout<<"=========== Acquisition Starting ============"< 1){ h->Rebin(REBIN_FACTOR); h->Scale(1.0/(float)REBIN_FACTOR); } // If the 2-D histo has not been created, then create it now TH2F *hh = h2[ichan]; if(hh == NULL && SAVE_WAVEFORMS){ int Nxbins = h->GetNbinsX(); float Xlo = h->GetBinLowEdge(1); float Xhi = h->GetBinLowEdge(Nxbins+1); int Nybins = Nevents; float Ylo = 0.5; float Yhi = (float)Nevents + 0.5; char hname[256]; sprintf(hname, "ch%d", ichan); h2[ichan] = hh = new TH2F(hname, hname, Nxbins, Xlo, Xhi, Nybins, Ylo, Yhi); hh->SetXTitle("time(ns)"); hh->SetYTitle("event number"); hh->SetZTitle("Amplitude (V)"); hh->SetStats(0); } // Loop over bins in waveform and copy into 2-D histo int idx = 1+((ichan-1)*3); for(int ibin = 1; ibin<= h->GetNbinsX(); ibin++){ // Copy to 2-D histo if specified if(hh)hh->SetBinContent(ibin, i+1, h->GetBinContent(ibin)); // Integrate value and gather average for pedestal float val = h->GetBinContent(ibin); vals[idx+0] += val; // Integral if(ibin<=20)vals[idx+1] += h->GetBinContent(ibin); // Pedestal } // Calculate pedestal from average of first 100 samples and // subtract from integral vals[idx+1] /= (float)100.0; // Pedestal vals[idx+0] -= vals[idx+1]*h->GetNbinsX(); // Most pulses are negative. Make the integral // and pedestal their opposites to have positive values vals[idx+0] *= -1.0; vals[idx+1] *= -1.0; // Find signal time by looking for fraction of signal maximum float max_val = 0.0; // used to get time for(int ibin = 1; ibin<= h->GetNbinsX(); ibin++){ // Find maximum pedestal subtracted (absolute) value float val = h->GetBinContent(ibin) - vals[idx+1]; if(fabs(val) > fabs(max_val))max_val = val; } float thresh = fabs(max_val)*TIMING_THRESHOLD_FRAC; for(int ibin = 1; ibin<= h->GetNbinsX(); ibin++){ // Find maximum pedestal subtracted (absolute) value float val = h->GetBinContent(ibin) - vals[idx+1]; if(fabs(val) >= thresh){ vals[idx+2] = h->GetBinCenter(ibin); break; } } delete h; } } cout<<"retrieved "<Fill(); for(int ii=0; ii0){ if(SCOPE_MODEL==TD3034){ string ret_str(buff); size_t pos = ret_str.find(""); if(pos!=string::npos){ if(ret_str[pos-1]=='1')run_state=true; } }else{ if(buff[0] == '1') run_state=true; } } return run_state; } //------------------------- // TriggerGetState //------------------------- string TriggerGetState(string scopeURL) { // This will connect to the scope and retrieve whether the trigger // is running or stopped. It returns true if the scope is accepting // triggers. // // NOTE: This does not mean a trigger has actually occurred, just whether // it is in the "Run" state or the "Stop" state. char buff[8192]; unsigned int Nbytes = SendCommand(scopeURL, "TRIGGER:STATE?", buff, 8192); string trig_state = "unknown"; if(Nbytes>0){ if(SCOPE_MODEL==TD3034){ string ret_str(buff); size_t pos = ret_str.find(""); if(pos!=string::npos){ size_t pos_end = pos; while(pos>0 && ret_str[pos]!='>')pos--; size_t pos_start = pos+1; string tmp(&ret_str[pos_start], pos_end - pos_start); trig_state = tmp; } }else{ string sbuff(buff); if(sbuff.find("ARMED") != string::npos ) trig_state = "ARMED"; if(sbuff.find("AUTO") != string::npos ) trig_state = "AUTO"; if(sbuff.find("READY") != string::npos ) trig_state = "READY"; if(sbuff.find("SAVE") != string::npos ) trig_state = "SAVE"; if(sbuff.find("TRIGGER") != string::npos) trig_state = "TRIGGER"; } } return trig_state; } //------------------------- // SendCommand //------------------------- unsigned int SendCommand(string scopeURL, string command, char *buff, unsigned int buff_size) { if(buff_size==0){ buff_size = 8192; buff = new char[buff_size]; } static bool connected = false; static int sockfd; if(!connected){ int portno; struct sockaddr_in serv_addr; struct hostent *server; portno = SOCKET_PORT; /* Create a socket point */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("ERROR opening socket"); return 0; } string ip_only = scopeURL.substr(7); server = gethostbyname(ip_only.c_str()); if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); return 0; } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); /* Now connect to the server */ if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { perror("ERROR connecting"); cerr << " IP: " << ip_only << endl; cerr << " port: " << portno << endl; return 0; } cout << " --- Connected to --- " <0){ cout << "(Read " << n << " residual bytes)" << endl; // cout << tmp << endl; } cout << endl; } int bytes_available = 0; ioctl(sockfd,FIONREAD,&bytes_available); if(bytes_available){ cout << "Bytes cluttering queue: " << bytes_available << endl; read(sockfd, buff, buff_size); cout <<" --- \"" << buff << "\"" << endl; } /* Send message to the server */ //_DBG_ << " Sending command: " << command << endl; command += "\n"; int n = write(sockfd, command.c_str(), command.length()); if (n < 0) { perror("ERROR writing to socket"); return 0; } if(command.find("CURVE?") != string::npos){ usleep(100000); // write(sockfd, "!r\n", 3); } // Wait for server to send us some bytes bzero(buff,buff_size); for(int i=0; i<1000; i++){ usleep(10000); bytes_available = 0; ioctl(sockfd,FIONREAD,&bytes_available); if(bytes_available > 0) break; if(i==500) write(sockfd, "!r\n", 3); } if(bytes_available<1){ _DBG_ << "Timeout waiting for response!" << endl; return 0; } /* Now read server response */ n = read(sockfd, buff, buff_size); string sbuff(buff); //_DBG_<<"bytes_available="<SetBinContent(++ibin, YMULT*(atof(item.c_str()) - YZERO)); break; } delete[] buff; return h; } //------------------------- // ReceiveData //------------------------- size_t ReceiveData( char *ptr, size_t size, size_t nmemb, DataBuffer_t *databuffer) { int Nbytes = size*nmemb; if(databuffer == NULL)return Nbytes; if(databuffer->buff == NULL)return Nbytes; if(databuffer->Nbytes+Nbytes > databuffer->buff_size){ cerr<<"Buffer not big enough to hold return data!!"<buff[databuffer->Nbytes], ptr, Nbytes); databuffer->Nbytes += Nbytes; //cout<<"received "<Nbytes<<" bytes total)"<>16; // shift bytes down to LSB #else unsigned char *cptr = (unsigned char *)sptr; unsigned char tmp = cptr[0]; cptr[0]=cptr[1]; cptr[1]=tmp; uint32_t val = *sptr++; #endif // Empirically, it was noticed that a pulse rose above 2^16, but was // of course truncated due to the 16bit words the data is stored in. // Adding a value to the 17th bit when a very low value was found seems // to restore the correct pulse shape. if(val<6000)val += (1<<16); // double dval = YMULT*(double)val - 7.76; // 7.76 is empirical double dval = YMULT*(double)val; h->SetBinContent(i+1, dval); //cout<<"val["<GetBinLowEdge(i+1)*1.0E-9<<"] = "<