/*********************************************************************************************** Basic functionality: The service function is called by the serviceThread object (of type ProcessThread). This call happens immediately after the previous call if the return value for the previous call was NORMAL. If the return value for the previous call was NOOP, then the serviceThread waits an amount of time defined in the serviceThread's constructor. SRI: To create a StreamSRI object, use the following code: std::string stream_id = "testStream"; BULKIO::StreamSRI sri = bulkio::sri::create(stream_id); Time: To create a PrecisionUTCTime object, use the following code: BULKIO::PrecisionUTCTime tstamp = bulkio::time::utils::now(); Ports: Data is passed to the serviceFunction through the getPacket call (BULKIO only). The dataTransfer class is a port-specific class, so each port implementing the BULKIO interface will have its own type-specific dataTransfer. The argument to the getPacket function is a floating point number that specifies the time to wait in seconds. A zero value is non-blocking. A negative value is blocking. Constants have been defined for these values, bulkio::Const::BLOCKING and bulkio::Const::NON_BLOCKING. Each received dataTransfer is owned by serviceFunction and *MUST* be explicitly deallocated. To send data using a BULKIO interface, a convenience interface has been added that takes a std::vector as the data input NOTE: If you have a BULKIO dataSDDS port, you must manually call "port->updateStats()" to update the port statistics when appropriate. Example: // this example assumes that the component has two ports: // A provides (input) port of type bulkio::InShortPort called short_in // A uses (output) port of type bulkio::OutFloatPort called float_out // The mapping between the port and the class is found // in the component base class header file bulkio::InShortPort::dataTransfer *tmp = short_in->getPacket(bulkio::Const::BLOCKING); if (not tmp) { // No data is available return NOOP; } std::vector<float> outputData; outputData.resize(tmp->dataBuffer.size()); for (unsigned int i=0; i<tmp->dataBuffer.size(); i++) { outputData[i] = (float)tmp->dataBuffer[i]; } // NOTE: You must make at least one valid pushSRI call if (tmp->sriChanged) { float_out->pushSRI(tmp->SRI); } float_out->pushPacket(outputData, tmp->T, tmp->EOS, tmp->streamID); delete tmp; // IMPORTANT: MUST RELEASE THE RECEIVED DATA BLOCK return NORMAL; If working with complex data (i.e., the "mode" on the SRI is set to true), the std::vector passed from/to BulkIO can be typecast to/from std::vector< std::complex<dataType> >. For example, for short data: bulkio::InShortPort::dataTransfer *tmp = myInput->getPacket(bulkio::Const::BLOCKING); std::vector<std::complex<short> >* intermediate = (std::vector<std::complex<short> >*) &(tmp->dataBuffer); // do work here std::vector<short>* output = (std::vector<short>*) intermediate; myOutput->pushPacket(*output, tmp->T, tmp->EOS, tmp->streamID); Interactions with non-BULKIO ports are left up to the component developer's discretion Properties: Properties are accessed directly as member variables. For example, if the property name is "baudRate", it may be accessed within member functions as "baudRate". Unnamed properties are given a generated name of the form "prop_n", where "n" is the ordinal number of the property in the PRF file. Property types are mapped to the nearest C++ type, (e.g. "string" becomes "std::string"). All generated properties are declared in the base class (fastfilter_base). Simple sequence properties are mapped to "std::vector" of the simple type. Struct properties, if used, are mapped to C++ structs defined in the generated file "struct_props.h". Field names are taken from the name in the properties file; if no name is given, a generated name of the form "field_n" is used, where "n" is the ordinal number of the field. Example: // This example makes use of the following Properties: // - A float value called scaleValue // - A boolean called scaleInput if (scaleInput) { dataOut[i] = dataIn[i] * scaleValue; } else { dataOut[i] = dataIn[i]; } A callback method can be associated with a property so that the method is called each time the property value changes. This is done by calling setPropertyChangeListener(<property name>, this, &fastfilter_i::<callback method>) in the constructor. Example: // This example makes use of the following Properties: // - A float value called scaleValue //Add to fastfilter.cpp fastfilter_i::fastfilter_i(const char *uuid, const char *label) : fastfilter_base(uuid, label) { setPropertyChangeListener("scaleValue", this, &fastfilter_i::scaleChanged); } void fastfilter_i::scaleChanged(const std::string& id){ std::cout << "scaleChanged scaleValue " << scaleValue << std::endl; } //Add to fastfilter.h void scaleChanged(const std::string&); ************************************************************************************************/ int fastfilter_i::serviceFunction() { bulkio::InFloatPort::dataTransfer *tmp = dataFloat_in->getPacket(bulkio::Const::BLOCKING); if (not tmp) { // No data is available return NOOP; } if (tmp->inputQueueFlushed) { LOG_WARN(fastfilter_i, "input queue flushed - data has been thrown on the floor. flushing internal buffers"); //flush all our processor states if the queue flushed boost::mutex::scoped_lock lock(filterLock_); for (map_type::iterator i = filters_.begin(); i!=filters_.end(); i++) i->second.filter->flush(); } bool updateSRI = tmp->sriChanged; float fs = 1.0/tmp->SRI.xdelta; { boost::mutex::scoped_lock lock(filterLock_); map_type::iterator i = filters_.find(tmp->streamID); firfilter* filter; if (i==filters_.end()) { //this is a new stream - need to create a new filter & wrapper LOG_DEBUG(fastfilter_i, "creating new filter for streamID "<<tmp->streamID); if (manualTaps_) { LOG_DEBUG(fastfilter_i, "using manual taps "); bool real, complex; getManualTaps(real,complex); if (real) filter = new firfilter(fftSize, realOut, complexOut, realTaps_); else if(complex) filter = new firfilter(fftSize, realOut, complexOut, complexTaps_); else { LOG_WARN(fastfilter_i, "state error - using manual taps with no filter provided. This shouldn't really happen"); if (updateSRI) dataFloat_out->pushSRI(tmp->SRI); dataFloat_out->pushPacket(tmp->dataBuffer, tmp->T, tmp->EOS, tmp->streamID); delete tmp; return NORMAL; } updateSRI = true; } else { LOG_DEBUG(fastfilter_i, "using filter designer"); correlationMode=false; if (filterProps.filterComplex) { designTaps(complexTaps_, fs); filter = new firfilter(fftSize, realOut, complexOut, complexTaps_); } else { designTaps(realTaps_, fs); filter = new firfilter(fftSize, realOut, complexOut, realTaps_); } } map_type::value_type filterWrapperMap(tmp->streamID, FilterWrapper()); i = filters_.insert(filters_.end(),filterWrapperMap); i->second.setParams(fs,filter); } else //get the filter we have used before filter = i->second.filter; //if we are in design mode and the sample rate has changed - redesign and apply our filter if (!manualTaps_ && i->second.hasSampleRateChanged(fs)) { if (filterProps.filterComplex) { designTaps(complexTaps_, fs); filter->setTaps(complexTaps_); } else { designTaps(realTaps_, fs); filter->setTaps(realTaps_); } } //now process the data if (tmp->SRI.mode==1) { //data is complex //run the filter std::vector<std::complex<float> >* cxData = (std::vector<std::complex<float> >*) &(tmp->dataBuffer); filter->newComplexData(*cxData); } else { //data is real //run the filter filter->newRealData(tmp->dataBuffer); //we might have a single complex frame if the previous data was complex and there were //complex data still in the filter taps if (!complexOut.empty()) { //update the mode to true for the complex fame and force an sri push tmp->SRI.mode=1; updateSRI = true; } } if (tmp->EOS) { //if we have an eos - remove the wrapper from the container filters_.erase(i); } } //to do -- adjust time stamps appropriately on all these output pushes // NOTE: You must make at least one valid pushSRI call if (updateSRI) { dataFloat_out->pushSRI(tmp->SRI); } if (!complexOut.empty()) { std::vector<float>* tmpRealOut = (std::vector<float>*) &(complexOut); dataFloat_out->pushPacket(*tmpRealOut, tmp->T, tmp->EOS, tmp->streamID); } if (!realOut.empty()) { //case we we forced a push on real data from previous complex data //need to force our sri back to real and push another sri if (updateSRI && tmp->SRI.mode==1) { //change mode back to 0 and send another sri with our real output tmp->SRI.mode=0; dataFloat_out->pushSRI(tmp->SRI); } dataFloat_out->pushPacket(realOut, tmp->T, tmp->EOS, tmp->streamID); } delete tmp; // IMPORTANT: MUST RELEASE THE RECEIVED DATA BLOCK return NORMAL; }
// Host code int main(int argc, char** argv) { ParseArguments(argc, argv); float s_SobelMatrix[25]; s_SobelMatrix[0] = 1; s_SobelMatrix[1] = 2; s_SobelMatrix[2]= 0; s_SobelMatrix[3] = -2; s_SobelMatrix[4] = -1; s_SobelMatrix[5] = 4; s_SobelMatrix[6] = 8; s_SobelMatrix[7] = 0; s_SobelMatrix[8] = -8; s_SobelMatrix[9] = -4; s_SobelMatrix[10] = 6; s_SobelMatrix[11] = 12; s_SobelMatrix[12] = 0; s_SobelMatrix[13] = -12; s_SobelMatrix[14] = -6; s_SobelMatrix[15] = 4; s_SobelMatrix[16] = 8; s_SobelMatrix[17] = 0; s_SobelMatrix[18] = -8; s_SobelMatrix[19] =-4; s_SobelMatrix[20] =1; s_SobelMatrix[21] =2; s_SobelMatrix[22] =0; s_SobelMatrix[23] =-2; s_SobelMatrix[24] =-1; unsigned char *palete = NULL; unsigned char *data = NULL, *out = NULL; PPMImage *input_image=NULL, *output_image=NULL; output_image = (PPMImage *)malloc(sizeof(PPMImage)); input_image = readPPM(PPMInFileL); printf("Running %s filter\n", Filter); out = (unsigned char *)malloc(); printf("Computing the CPU output\n"); printf("Image details: %d by %d = %d , imagesize = %d\n", input_image->x, input_image->y, input_image->x * input_image->y, input_image->x * input_image->y); cutilCheckError(cutStartTimer(time_CPU)); if(FilterMode == SOBEL_FILTER){ printf("Running Sobel\n"); CPU_Sobel(intput_image->data, output_image, input_image->x, input_image->y); } else if(FilterMode == HIGH_BOOST_FILTER){ printf("Running boost\n"); CPU_Boost(data, out, dib.width, dib.height); } cutilCheckError(cutStopTimer(time_CPU)); if(FilterMode == SOBEL_FILTER || FilterMode == SOBEL_FILTER5) BitMapWrite("CPU_sobel.bmp", &bmp, &dib, out, palete); else if(FilterMode == AVERAGE_FILTER) BitMapWrite("CPU_average.bmp", &bmp, &dib, out, palete); else if(FilterMode == HIGH_BOOST_FILTER) BitMapWrite("CPU_boost.bmp", &bmp, &dib, out, palete); printf("Done with CPU output\n"); printf("CPU execution time %f \n", cutGetTimerValue(time_CPU)); printf("Allocating %d bytes for image \n", dib.image_size); cutilSafeCall( cudaMalloc( (void **)&d_In, dib.image_size*sizeof(unsigned char)) ); cutilSafeCall( cudaMalloc( (void **)&d_Out, dib.image_size*sizeof(unsigned char)) ); // creating space for filter matrix cutilSafeCall( cudaMalloc( (void **)&sobel_matrix, 25*sizeof(float)) ); cutilCheckError(cutStartTimer(time_mem)); cudaMemcpy(d_In, data, dib.image_size*sizeof(unsigned char), cudaMemcpyHostToDevice); cudaMemcpy(sobel_matrix, s_SobelMatrix, 25*sizeof(float), cudaMemcpyHostToDevice); cutilCheckError(cutStopTimer(time_mem)); FilterWrapper(data, dib.width, dib.height); // Copy image back to host cutilCheckError(cutStartTimer(time_mem)); cudaMemcpy(out, d_Out, dib.image_size*sizeof(unsigned char), cudaMemcpyDeviceToHost); cutilCheckError(cutStopTimer(time_mem)); printf("GPU execution time %f Memtime %f \n", cutGetTimerValue(time_GPU), cutGetTimerValue(time_mem)); printf("Total GPU = %f \n", (cutGetTimerValue(time_GPU) + cutGetTimerValue(time_mem))); // Write output image BitMapWrite(BMPOutFile, &bmp, &dib, out, palete); Cleanup(); }