template <  typename IN_PORT_TYPE, typename OUT_PORT_TYPE > int copy_bb_base::_forecastAndProcess( bool &eos, typename  std::vector< gr_istream< IN_PORT_TYPE > > &istreams ,
                                 typename  std::vector< gr_ostream< OUT_PORT_TYPE > > &ostreams  )
{
    typedef typename std::vector< gr_istream< IN_PORT_TYPE > >   _IStreamList;
    typedef typename std::vector< gr_ostream< OUT_PORT_TYPE > >  _OStreamList;

    typename _OStreamList::iterator ostream;
    typename _IStreamList::iterator istream = istreams.begin();
    int nout = 0;
    bool dataReady = false;
    if ( !eos ) {
        uint64_t max_items_avail = 0;
        for ( int idx=0 ; istream != istreams.end() && serviceThread->threadRunning() ; idx++, istream++ ) {
            LOG_TRACE( copy_bb_base, "GET MAX ITEMS: STREAM:"<< idx << " NITEMS/SCALARS:" << 
                       istream->nitems() << "/" << istream->_data.size() );
            max_items_avail = std::max( istream->nitems(), max_items_avail );
        }

        if ( max_items_avail == 0  ) {
            LOG_TRACE( copy_bb_base, "DATA CHECK - MAX ITEMS  NOUTPUT/MAX_ITEMS:" <<   noutput_items << "/" << max_items_avail);
            return -1;
        }

        //
        // calc number of output elements based on input items available
        //
        noutput_items = 0;
        if ( !gr_sptr->fixed_rate() )  {
            noutput_items = round_down((int32_t) (max_items_avail * gr_sptr->relative_rate()), gr_sptr->output_multiple());
            LOG_TRACE( copy_bb_base, " VARIABLE FORECAST NOUTPUT == " << noutput_items );
        } else {
            istream = istreams.begin();
            for ( int i=0; istream != istreams.end(); i++, istream++ ) {
                int t_noutput_items = gr_sptr->fixed_rate_ninput_to_noutput( istream->nitems() );
                if ( gr_sptr->output_multiple_set() ) {
                    t_noutput_items = round_up(t_noutput_items, gr_sptr->output_multiple());
                }
                if ( t_noutput_items > 0 ) {
                    if ( noutput_items == 0 ) {
                        noutput_items = t_noutput_items;
                    }
                    if ( t_noutput_items <= noutput_items ) {
                        noutput_items = t_noutput_items;
                    }
                }
            }
            LOG_TRACE( copy_bb_base,  " FIXED FORECAST NOUTPUT/output_multiple == " << 
                        noutput_items  << "/" << gr_sptr->output_multiple());
        }

        //
        // ask the block how much input they need to produce noutput_items...
        // if enough data is available to process then set the dataReady flag
        //
        int32_t  outMultiple = gr_sptr->output_multiple();
        while ( !dataReady && noutput_items >= outMultiple  ) {
            //
            // ask the block how much input they need to produce noutput_items...
            //
            gr_sptr->forecast(noutput_items, _ninput_items_required);

            LOG_TRACE( copy_bb_base, "--> FORECAST IN/OUT " << _ninput_items_required[0]  << "/" << noutput_items  );

            istream = istreams.begin();
            uint32_t dr_cnt=0;
            for ( int idx=0 ; noutput_items > 0 && istream != istreams.end(); idx++, istream++ ) {
                // check if buffer has enough elements
                _input_ready[idx] = false;
                if ( istream->nitems() >= (uint64_t)_ninput_items_required[idx] ) {
                    _input_ready[idx] = true;
                    dr_cnt++;
                }
                LOG_TRACE( copy_bb_base, "ISTREAM DATACHECK NELMS/NITEMS/REQ/READY:" <<   istream->nelems() << 
                          "/" << istream->nitems() << "/" << _ninput_items_required[idx] << "/" << _input_ready[idx]);
            }
    
            if ( dr_cnt < istreams.size() ) {
                if ( outMultiple > 1 ) {
                    noutput_items -= outMultiple;
                } else {
                    noutput_items /= 2;
                }
            } else {
                dataReady = true;
            }
            LOG_TRACE( copy_bb_base, " TRIM FORECAST NOUTPUT/READY " << noutput_items << "/" << dataReady );
        }

        // check if data is ready...
        if ( !dataReady ) {
            LOG_TRACE( copy_bb_base, "DATA CHECK - NOT ENOUGH DATA  AVAIL/REQ:" <<   _istreams[0].nitems() << 
                      "/" << _ninput_items_required[0] );
            return -1;
        }

        // reset looping variables
        int  ritems = 0;
        int  nitems = 0;

        // reset caching vectors
        _output_items.clear();
        _input_items.clear();
        _ninput_items.clear();
        istream = istreams.begin();

        for ( int idx=0 ; istream != istreams.end(); idx++, istream++ ) {
            // check if the stream is ready
            if ( !_input_ready[idx] ) {
                continue;
            }
            // get number of items remaining
            try {
                ritems = gr_sptr->nitems_read( idx );
            } catch(...){
                // something bad has happened, we are missing an input stream
                LOG_ERROR( copy_bb_base, "MISSING INPUT STREAM FOR GR BLOCK, STREAM ID:" <<   istream->streamID );
                return -2;
            } 
    
            nitems = istream->nitems() - ritems;
            LOG_TRACE( copy_bb_base,  " ISTREAM: IDX:" << idx  << " ITEMS AVAIL/READ/REQ " << nitems << "/" 
                       << ritems << "/" << _ninput_items_required[idx] );
            if ( nitems >= _ninput_items_required[idx] && nitems > 0 ) {
                //remove eos checks ...if ( nitems < _ninput_items_required[idx] ) nitems=0;
                _ninput_items.push_back( nitems );
                _input_items.push_back( (const void *) (istream->read_pointer(ritems)) );
            }
        }

        //
        // setup output buffer vector based on noutput..
        //
        ostream = ostreams.begin();
        for( ; ostream != ostreams.end(); ostream++ ) {
            ostream->resize(noutput_items);
            _output_items.push_back((void*)(ostream->write_pointer()) );
        }

        nout=0;
        if ( _input_items.size() != 0 && serviceThread->threadRunning() ) {
            LOG_TRACE( copy_bb_base, " CALLING WORK.....N_OUT:" << noutput_items << " N_IN:" << nitems 
                      << " ISTREAMS:" << _input_items.size() << " OSTREAMS:" << _output_items.size());
            nout = gr_sptr->general_work( noutput_items, _ninput_items, _input_items, _output_items);
            LOG_TRACE( copy_bb_base, "RETURN  WORK ..... N_OUT:" << nout);
        }

        // check for stop condition from work method
        if ( nout < gr_block::WORK_DONE ) {
            LOG_WARN( copy_bb_base, "WORK RETURNED STOP CONDITION..." << nout );
            nout=0;
            eos = true;
        }
    }

    if (nout != 0 or eos ) {
        noutput_items = nout;
        LOG_TRACE( copy_bb_base, " WORK RETURNED: NOUT : " << nout << " EOS:" << eos);
        ostream = ostreams.begin();
        typename IN_PORT_TYPE::dataTransfer *pkt=NULL;
        for ( int idx=0 ; ostream != ostreams.end(); idx++, ostream++ ) {

            pkt=NULL;
            int inputIdx = idx;
            if ( (size_t)(inputIdx) >= istreams.size() ) {
                for ( inputIdx= istreams.size()-1; inputIdx > -1; inputIdx--) {
                    if ( istreams[inputIdx].pkt != NULL ) {
                        pkt = istreams[inputIdx].pkt;
                        break;
                    }
                }
            } else {
                pkt = istreams[inputIdx].pkt;
            }

            LOG_TRACE( copy_bb_base,  "PUSHING DATA   ITEMS/STREAM_ID " << ostream->nitems() << "/" << ostream->streamID );    
            if ( _maintainTimeStamp ) {

                // set time stamp for output samples based on input time stamp
                if ( ostream->nelems() == 0 )  {
#ifdef TEST_TIME_STAMP
      LOG_DEBUG( copy_bb_base, "SEED - TS SRI:  xdelta:" << std::setprecision(12) << ostream->sri.xdelta );
      LOG_DEBUG( copy_bb_base, "OSTREAM WRITE:   maint:" << _maintainTimeStamp );
      LOG_DEBUG( copy_bb_base, "                  mode:" <<  ostream->tstamp.tcmode );
      LOG_DEBUG( copy_bb_base, "                status:" <<  ostream->tstamp.tcstatus );
      LOG_DEBUG( copy_bb_base, "                offset:" <<  ostream->tstamp.toff );
      LOG_DEBUG( copy_bb_base, "                 whole:" <<  std::setprecision(10) << ostream->tstamp.twsec );
      LOG_DEBUG( copy_bb_base, "SEED - TS         frac:" <<  std::setprecision(12) << ostream->tstamp.tfsec );
#endif
                    ostream->setTimeStamp( pkt->T, _maintainTimeStamp );
                }

                // write out samples, and set next time stamp based on xdelta and  noutput_items
                ostream->write ( noutput_items, eos );
            } else {
// use incoming packet's time stamp to forward
                if ( pkt ) {
#ifdef TEST_TIME_STAMP
      LOG_DEBUG( copy_bb_base, "OSTREAM  SRI:  items/xdelta:" << noutput_items << "/" << std::setprecision(12) << ostream->sri.xdelta );
      LOG_DEBUG( copy_bb_base, "PKT - TS         maint:" << _maintainTimeStamp );
      LOG_DEBUG( copy_bb_base, "                  mode:" <<  pkt->T.tcmode );
      LOG_DEBUG( copy_bb_base, "                status:" <<  pkt->T.tcstatus );
      LOG_DEBUG( copy_bb_base, "                offset:" <<  pkt->T.toff );
      LOG_DEBUG( copy_bb_base, "                 whole:" <<  std::setprecision(10) << pkt->T.twsec );
      LOG_DEBUG( copy_bb_base, "PKT - TS          frac:" <<  std::setprecision(12) << pkt->T.tfsec );
#endif
                    ostream->write( noutput_items, eos, pkt->T  );
                } else {
#ifdef TEST_TIME_STAMP
      LOG_DEBUG( copy_bb_base, "OSTREAM  SRI:  items/xdelta:" << noutput_items << "/" << std::setprecision(12) << ostream->sri.xdelta );
      LOG_DEBUG( copy_bb_base, "OSTREAM TOD      maint:" << _maintainTimeStamp );
      LOG_DEBUG( copy_bb_base, "                  mode:" <<  ostream->tstamp.tcmode );
      LOG_DEBUG( copy_bb_base, "                status:" <<  ostream->tstamp.tcstatus );
      LOG_DEBUG( copy_bb_base, "                offset:" <<  ostream->tstamp.toff );
      LOG_DEBUG( copy_bb_base, "                 whole:" <<  std::setprecision(10) << ostream->tstamp.twsec );
      LOG_DEBUG( copy_bb_base, "OSTREAM TOD       frac:" <<  std::setprecision(12) << ostream->tstamp.tfsec );
#endif
                    // use time of day as time stamp
                    ostream->write( noutput_items, eos,  _maintainTimeStamp );
                }
            }

        } // for ostreams
    }

    return nout;     
}
template <  typename IN_PORT_TYPE > int tagged_file_sink_b_base::_forecastAndProcess( bool &eos, typename  std::vector< gr_istream< IN_PORT_TYPE > > &istreams )
{
    typedef typename std::vector< gr_istream< IN_PORT_TYPE > >   _IStreamList;

    typename _IStreamList::iterator istream = istreams.begin();
    int nout = 0;
    bool dataReady = false;
    if ( !eos ) {
        uint64_t max_items_avail = 0;
        for ( int idx=0 ; istream != istreams.end() && serviceThread->threadRunning() ; idx++, istream++ ) {
            LOG_TRACE( tagged_file_sink_b_base, "GET MAX ITEMS: STREAM:" << idx << " NITEMS/SCALARS:"
                      << istream->nitems() << "/" << istream->_data.size() );
            max_items_avail = std::max( istream->nitems(), max_items_avail );
        }

        //
        // calc number of output items to produce
        //
        noutput_items = (int) (max_items_avail * gr_sptr->relative_rate ());
        noutput_items = round_down (noutput_items, gr_sptr->output_multiple ());

        if ( noutput_items <= 0  ) {
           LOG_TRACE( tagged_file_sink_b_base, "DATA CHECK - MAX ITEMS  NOUTPUT/MAX_ITEMS:" <<   noutput_items << "/" << max_items_avail);
           return -1;
        }

        if ( gr_sptr->fixed_rate() ) {
            istream = istreams.begin();
            for ( int i=0; istream != istreams.end(); i++, istream++ ) {
                int t_noutput_items = gr_sptr->fixed_rate_ninput_to_noutput( istream->nitems() );
                if ( gr_sptr->output_multiple_set() ) {
                    t_noutput_items = round_up(t_noutput_items, gr_sptr->output_multiple());
                }
                if ( t_noutput_items > 0 ) {
                    if ( noutput_items == 0 ) {
                        noutput_items = t_noutput_items;
                    }
                    if ( t_noutput_items <= noutput_items ) {
                        noutput_items = t_noutput_items;
                    }
                }
            }
            LOG_TRACE( tagged_file_sink_b_base, " FIXED FORECAST NOUTPUT/output_multiple == " 
                      << noutput_items  << "/" << gr_sptr->output_multiple());
        }

        //
        // ask the block how much input they need to produce noutput_items...
        // if enough data is available to process then set the dataReady flag
        //
        int32_t  outMultiple = gr_sptr->output_multiple();
        while ( !dataReady && noutput_items >= outMultiple  ) {
            //
            // ask the block how much input they need to produce noutput_items...
            //
            gr_sptr->forecast(noutput_items, _ninput_items_required);

            LOG_TRACE( tagged_file_sink_b_base, "--> FORECAST IN/OUT " << _ninput_items_required[0]  << "/" << noutput_items  );

            istream = istreams.begin();
            uint32_t dr_cnt=0;
            for ( int idx=0 ; noutput_items > 0 && istream != istreams.end(); idx++, istream++ ) {
                // check if buffer has enough elements
                _input_ready[idx] = false;
                if ( istream->nitems() >= (uint64_t)_ninput_items_required[idx] ) {
                    _input_ready[idx] = true;
                    dr_cnt++;
                }
                LOG_TRACE( tagged_file_sink_b_base, "ISTREAM DATACHECK NELMS/NITEMS/REQ/READY:" << 
                          istream->nelems() << "/" << istream->nitems() << "/" << 
                          _ninput_items_required[idx] << "/" << _input_ready[idx]);
            }
    
            if ( dr_cnt < istreams.size() ) {
                if ( outMultiple > 1 ) {
                    noutput_items -= outMultiple;
                } else {
                    noutput_items /= 2;
                }
            } else {
                dataReady = true;
            }
            LOG_TRACE( tagged_file_sink_b_base, " TRIM FORECAST NOUTPUT/READY " << noutput_items << "/" << dataReady );
        }

        // check if data is ready...
        if ( !dataReady ) {
            LOG_TRACE( tagged_file_sink_b_base, "DATA CHECK - NOT ENOUGH DATA  AVAIL/REQ:" 
                      <<   _istreams[0].nitems() << "/" << _ninput_items_required[0] );
            return -1;
        }

        // reset looping variables
        int  ritems = 0;
        int  nitems = 0;

        // reset caching vectors
        _output_items.clear();
        _input_items.clear();
        _ninput_items.clear();
        istream = istreams.begin();

        for ( int idx=0 ; istream != istreams.end(); idx++, istream++ ) {
            // check if the stream is ready
            if ( !_input_ready[idx] ) continue;
      
            // get number of items remaining
            try {
                ritems = gr_sptr->nitems_read( idx );
            } catch(...){
                // something bad has happened, we are missing an input stream
                LOG_ERROR( tagged_file_sink_b_base, "MISSING INPUT STREAM FOR GR BLOCK, STREAM ID:" <<   istream->streamID );
                return -2;
            } 

            nitems = istream->nitems() - ritems;
            LOG_TRACE( tagged_file_sink_b_base,  " ISTREAM: IDX:" << idx  << " ITEMS AVAIL/READ/REQ " << nitems << "/" 
                      << ritems << "/" << _ninput_items_required[idx] );
            if ( nitems >= _ninput_items_required[idx] && nitems > 0 ) {
                //remove eos checks ...if ( nitems < _ninput_items_required[idx] ) nitems=0;
                _ninput_items.push_back( nitems );
                _input_items.push_back( (const void *) (istream->read_pointer(ritems)) );
            }
        }

        nout=0;
        if ( _input_items.size() != 0 && serviceThread->threadRunning() ) {
            LOG_TRACE( tagged_file_sink_b_base, " CALLING WORK.....N_OUT:" << noutput_items << 
                      " N_IN:" << nitems << " ISTREAMS:" << _input_items.size() << 
                      " OSTREAMS:" << _output_items.size());
            nout = gr_sptr->general_work( noutput_items, _ninput_items, _input_items, _output_items);

            // sink/analyzer patterns do not return items, so consume_each is not called in Gnu Radio BLOCK
            if ( nout == 0 ) {
                gr_sptr->consume_each(nitems);
            }

            LOG_TRACE( tagged_file_sink_b_base, "RETURN  WORK ..... N_OUT:" << nout);
        }

        // check for stop condition from work method
        if ( nout < gr_block::WORK_DONE ) {
            LOG_WARN( tagged_file_sink_b_base, "WORK RETURNED STOP CONDITION..." << nout );
            nout=0;
            eos = true;
        }
    }

    return nout;
 
}