/* * FCALLEDPulser.cpp * * Implementation of the class for FCAL pulser channels. * * Created on: Aug 26, 2015 * Author: Hovanes Egiyan */ #include "FCALLEDPulser.hh" using namespace std; // The pulse width of the pulser is given in 10ns units. // This is the conversion constant between 1ns and 10ns double FCALLEDPulser::flpPulseWidthUnit = 10.0; // The pulse period of the pulser is given in 10ns units. // This is the conversion constant between 1s and 10ns double FCALLEDPulser::flpPulsePeriodUnit = 1.0e-08; // Explicitly instantiate the template methods from clas FCALLEDPulser template short FCALLEDPulser::getAverageValue( const std::string pvLabel ); template unsigned FCALLEDPulser::getAverageValue( const std::string pvLabel ); template void FCALLEDPulser::setAllChannels( const std::string pvLabel, const std::string rbLabel, const short value ) ; template void FCALLEDPulser::setAllChannels( const std::string pvLabel, const std::string rbLabel, const unsigned value ) ; // Returned the averaged value of the unsigned values. The // averaged value is over all pulser board channels associated // with this pulser. template T FCALLEDPulser::getAverageValue( const std::string pvLabel ) { double sum = 0.0; // Runinv sum variable // Loop over all the pulser board channels related to this display and // sum their values. for( unsigned iChan = 0; iChan < getChannelVector().size(); iChan++ ) { // Append the channel number after a colon to the label. stringstream ssAddr; ssAddr << getChannelVector()[iChan]; std::string addr = ssAddr.str(); std::string label = pvLabel + ":" + addr ; double currValue = vpPV->getPV( label, SYNC, T() ); sum += currValue; } if( getChannelVector().size() > 0 ) { return static_cast( round( sum / getChannelVector().size() ) ); } else { // If no pulser board channel is assigned to this pulser // that is a pretty bad error, throw an exception std::stringstream errStream; errStream << "FCALLEDPulser::getAverageValue : Pulser object " << vpID << " does not have channels associated with it" ; throw std::runtime_error( errStream.str() ); } } // Set the value of pvLabel parameter PV for all related board channels of the pulser // to the value. Afterwards, verify that the value is set by checking the readback // paramter PV rbLabel. template void FCALLEDPulser::setAllChannels( const std::string pvLabel, const std::string rbLabel, const T value ) { // Set all PV directly involved in this pulser for( unsigned iChan = 0; iChan < getChannelVector().size(); iChan++ ) { // Append the channel number after a colon to the label. stringstream ssAddr; ssAddr << getChannelVector()[iChan]; std::string addr = ssAddr.str(); std::string label = pvLabel + ":" + addr ; std::string readbackLabel = rbLabel + ":" + addr ; vpPV->setValue( label, value, SYNC ); // check if the pvSet succeeded, and write a message on the screen // if the readback did not match the setpoint. if( !vpPV->gotReadback( value, readbackLabel ) ) { cout << "FCALLEDPulser::setAllChannels : failed to set PV with label " << pvLabel << " for " << vpID << " to " << value << endl; } } return; } // A method that knows stuff about this pulsers and sets it's state to // what is in "state" argument. void FCALLEDPulser::switchState( const short state ) { unsigned oldStartMask = vpPV->getPV( "START_MASK_R", SYNC, unsigned() ); unsigned oldStopMask = vpPV->getPV( "STOP_MASK_R", SYNC, unsigned() ); cout << "Will switch state to " << state << endl; cout << "Old start mask is " << oldStartMask << " , old stop mask is " << oldStopMask << endl; if( state == 1 ) { // Request was to turn this pulser OFF. // OR the current stop mask with the mask range that this // pulser is allowed to change. After setting stop masks in the allowed // change range, and it with the NOT of the channel mask showing the ' // channels that need to be turned on. The result shows which board channels // will need to be turned off before turning the channels on. unsigned stopMask = ( oldStopMask | getRangeMask() ) & (~getChannelMask()); cout << "Setting new stop mask " << stopMask << endl; vpPV->setValue( "STOP_MASK_W", static_cast(stopMask), SYNC ); // check if the pvSet succeeded, and write a message on the screen // if the readback did not match the setpoint. if( !vpPV->gotReadback( stopMask, "STOP_MASK_R") ) { cout << "FCALLEDPulser::switchState : failed to set stopmask for " << vpID << " to " << stopMask << endl; } // And the current start mask with the NOT of the mask showing the range of // change for this pulser. Then or it with the channel mask showing which channels // need to be turned on. This will set the start mask that includes the channels // in the allowed range. unsigned startMask = ( oldStartMask & (~getRangeMask()) ) | getChannelMask() ; cout << "Setting new start mask " << startMask << endl; vpPV->setValue( "START_MASK_W", static_cast(startMask), SYNC ); // check if the pvSet succeeded, and write a message on the screen // if the readback did not match the setpoint. if( !vpPV->gotReadback( startMask, "START_MASK_R") ) { cout << "FCALLEDPulser::switchState : failed to set startmask for " << vpID << " to " << startMask << endl; } // Stop the pulser using the new stopmask vpPV->setValue( "SWITCH", static_cast(0), SYNC ); if( !vpPV->gotReadback( static_cast(0), "STATUS") ) { cout << "FCALLEDPulser::switchState : failed to switch " << vpID << " OFF before turning it on " << endl; } // Start the pulser with the new startmask vpPV->setValue( "SWITCH", static_cast(1), SYNC ); if( !vpPV->gotReadback( static_cast(1), "STATUS") ) { cout << "FCALLEDPulser::switchState : failed to switch " << vpID << " ON" << endl; } } else if( state == 0 ){ // Request was to turn this pulser OFF // OR the current stop mask with the channel mask showing which channels // are getting turned off. unsigned stopMask = oldStopMask | getChannelMask(); vpPV->setValue( "STOP_MASK_W", static_cast(stopMask), SYNC ); if( !vpPV->gotReadback( stopMask, "STOP_MASK_R") ) { cout << "FCALLEDPulser::switchState : failed to set stopmask for " << vpID << " to " << stopMask << endl; } // AND the current start mask with the NOT of the channel mask. This value will // be used in the next operations of switching. unsigned startMask = oldStartMask & (~getChannelMask()) ; vpPV->setValue( "START_MASK_W", static_cast(startMask), SYNC ); // check if the pvSet succeeded, and write a message on the screen // if the readback did not match the setpoint. if( !vpPV->gotReadback( startMask, "START_MASK_R") ) { cout << "FCALLEDPulser::switchState : failed to set startmask for " << vpID << " to " << startMask << endl; } // Stop the pulser vpPV->setValue( "SWITCH", static_cast(0), SYNC ); if( !vpPV->gotReadback( static_cast(0), "STATUS") ) { cout << "FCALLEDPulser::switchState : failed switch " << vpID << " OFF " << endl; } } return; } // Set the pulse width for the pulser void FCALLEDPulser::setWidth( const unsigned width ) { if( flpPulseWidthUnit > 0 ) { // convert ns into 10ns units for the PV, its a simple scale factor unsigned widthPV = static_cast (round(width/flpPulseWidthUnit) ); // Set all channels to the value given in 10ns units that // the EPICS PV (and the firmware expects). setAllChannels( "WIDTH_W", "WIDTH_R", widthPV ); } } // Set the pulsing frequency for the pulser by setting the period PVs void FCALLEDPulser::setFrequency( const double freq ) { cout << "Received request for frequency = " << freq << endl; double period2set = 0; // Need to convert the frequency to period . if( freq > 0 ) { // Period is measured in 10ns, the formula is "Pulser Period = (PERIOD+1)*10ns" period2set = ( (1.0/flpPulsePeriodUnit) / freq ) - 1; } else { std::stringstream errStream; errStream << "FCALLEDPulser::setFrequency : Pulser object " << vpID << " requested to set bad frequency value " << freq ; throw std::runtime_error( errStream.str() ); } cout << "Setting period to " << period2set << endl; // Set period to period2set using PV with PERIOD_W label and verify // the readback using PV with PERIOD_R label setAllChannels( "PERIOD_W", "PERIOD_R", static_cast( round(period2set) ) ); } // Set the number of pulses for the pulser void FCALLEDPulser::setNPulses( const unsigned n ) { setAllChannels( "NPULSES_W", "NPULSES_R", n ); } // Return the status of the pulser short FCALLEDPulser::getStatus() { // Read the current status unsigned currentStatus = vpPV->getPV("STATUS", SYNC, unsigned() ); // Mask the current status with the channel mask to see // the status part related to the mask range for this pulser unsigned maskedWord = getChannelMask() & currentStatus; // If any of the channels is not ON, then the pulser is not running, return 0. // If all channels required in the pulser are on, then pulser is on, return 1. return (maskedWord == getChannelMask()) ? 1 : 0; } // Return the width of the pulser unsigned FCALLEDPulser::getWidth() { // Get the average width in 10ns units which are used in the EPICS PV // as well as in the V1495 firmware. unsigned widthPV = getAverageValue( "WIDTH_R" ); // Return the average value of all pulser board channels associated with this pulser. return static_cast ( round( widthPV * flpPulseWidthUnit ) ) ; } // Return the frequency of the pulser based on the period PVs double FCALLEDPulser::getFrequency() { // Get the average value of all pulser board channels associated with // this pulser. unsigned currentPeriod = getAverageValue( "PERIOD_R" ); // Now convert the period to frequency if( currentPeriod >= 0 ) { // Period is measured in 10ns, the formula is "Pulser Period = (PERIOD+1)*10ns" return (1.0/flpPulsePeriodUnit) / (currentPeriod+1); } else { cout << "FCALLEDPulser::getFrequency: current period for pulser " << vpID << " is " << currentPeriod << " ns" << endl; return 0; } } // Return the number of pulses for the pulser unsigned FCALLEDPulser::getNPulses() { // Return the average value of all pulser board channels associated with this pulser. return getAverageValue( "NPULSES_R" ) ; }