www.pudn.com > qtdso-0.3.1.rar > dsowid.cpp


//======================================================================
// File:		dsowid.cpp
// Author:	Matthias Toussaint
// Created:	Sun Jun  9 22:28:27 CEST 2002
//----------------------------------------------------------------------
// Permission to use, copy, modify, and distribute this software and its
// documentation  for any  purpose and  without fee is  hereby  granted,
// provided  that below copyright notice appear  in all copies  and that
// both  that  copyright  notice and  this permission  notice  appear in
// supporting documentation.
// 
// This  file is  provided AS IS  with no  warranties  of any kind.  The
// author shall  have no liability  with respect  to the infringement of
// copyrights, trade  secrets  or any patents by  this file  or any part
// thereof.  In no event will the author be liable  for any lost revenue
// or profits or other special, indirect and consequential damages.
//----------------------------------------------------------------------
// (c) 2000-2002 Matthias Toussaint
//======================================================================

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 

//#include 
//#include 
//#include 

QColor DsoWid::bgColor = Qt::black;
QColor DsoWid::borderColor = Qt::white;
QColor DsoWid::gridColor = Qt::darkGray;
QColor DsoWid::chColor[3] = { QColor( 50, 255, 50 ), QColor( 255, 255, 50 ), QColor(0,0,0) };
QColor DsoWid::chChangedColor[3] = { QColor( 255, 150, 255 ), QColor( 150, 255, 255 ), QColor(0,0,0) };
QColor DsoWid::addColor = QColor( 180, 180, 255 );
QColor DsoWid::subColor = QColor( 255, 180, 180 );
QColor DsoWid::amplitudeColor = Qt::cyan;
QColor DsoWid::timeColor = Qt::cyan;
int    DsoWid::s_lineWidth = 3;
int    DsoWid::s_lineWidthFft = 0;
                 
DsoWid::DsoWid( QWidget *parent, const char *name ) :
  QWidget( parent, name ),
  m_dso( 0 ),
  m_xOff( 768 ),
  m_timeMarkerMode( Off ),
  m_showAmplitudeMarker( false ),
  m_mathMode( NoMath ),
  m_moving( DsoWid::None ),
  m_mode( DsoWid::DSO ),
  m_drawTriggerLine( false ),
  m_fftWindow( 0 ),
  m_fftZoom( 2 ),
  m_fftPostMag( 1. ),
  m_fftShowPeaks( false ),
  m_fftType( DsoWid::Power ),
  m_sinXRegister( 0 ),
  m_sinXCoeff( 0 ),
  m_sinXLength( 0 ),
  m_sinXPointer( 0 ),
  m_interpolation( Linear ),
  m_fftDisplayMode( RT ),
  m_updateTimer( -1 ),
  m_frameRate( FR30 ),
  m_continuousSampling( false ),
  m_wisdomString( 0 ),
  m_fftAverageBufferLength( 100 ),
  m_fftGridMode( Lin ),
  m_drawMeasuredVolts( true ),
  m_hasData( false ),
  m_comeAgainTimer( -1 ),
  m_realFrameRate( 30 ),
  m_simWid( 0 ),
  m_model( Dso::Unknown ),
  m_showEnvelope( false ),
  m_dataStretch( 1.0 ),
  m_triggerStabilizer( false ),
  m_converterHistogram( 0 )
{
  m_fftDrawMode[0] = m_fftDrawMode[1] = Line;
  m_data[0] = m_data[1] = m_data[2] = 0;
  m_arr = m_arr2 = m_arr3 = 0;
  
  m_invCh[2] = m_absCh[2] = false;
  
  setChannelOpSLOT( false, false, false, false );
  
  // load wisdom if avail
  //
/*  QFile file( "/home/mt/qtdso_wisdom" );
  
  if (file.open( IO_ReadOnly ))
  {
    QString wisdomString;
    
    QTextStream ts( &file );
    ts >> wisdomString;
    file.close();
    
    fftw_import_wisdom_from_string( wisdomString.latin1() );
  }
  else
  {
    m_fftw_plan = fftw_create_plan( 4096, FFTW_FORWARD,
                                    FFTW_MEASURE | FFTW_USE_WISDOM );
  }
  */
  
  m_showChannel[0] = m_showChannel[1] = true;
  m_showChannel[2] = false;  // math
  m_stretch[0] = m_stretch[1] = m_stretch[2] = 1.0;  
  m_tb[DSO] = m_tb[XY] = m_tb[FFT] = Dso::TB1ms;  
  m_dcOffset[0] = m_dcOffset[1] = m_dcOffset[2] = 0;
  
  m_timeMarker[0] = 16;
  m_timeMarker[1] = 240;
  m_timeMarker[2] = 464;
  m_freqMarker = 64;
  m_amplitudeMarker[0] = 16;
  m_amplitudeMarker[1] = 240;
  m_dbMarker[0] = 16;
  m_dbMarker[1] = 240;
  m_yOff[0] = -80;
  m_yOff[1] = 80;
  m_yOff[2] = 0;
  
  setBackgroundMode( QWidget::NoBackground );
      
  m_fftOut = new float [FFT_SIZE_MAX/2];
  
  m_fftOutAvPointer[0] = 0;
  m_fftOutAvPointer[1] = 0;
  
  m_fftOutSum[0] = new float [FFT_SIZE_MAX/2];
  m_fftOutSum[1] = new float [FFT_SIZE_MAX/2];
  
  m_fftwIn  = (fftw_complex *)fftw_malloc( sizeof(fftw_complex)*FFT_SIZE_MAX );
  m_fftwOut = (fftw_complex *)fftw_malloc( sizeof(fftw_complex)*FFT_SIZE_MAX );
    
  m_fftw_plan = fftw_plan_dft_1d( 4096, m_fftwIn, m_fftwOut,
                                  FFTW_FORWARD, FFTW_ESTIMATE );
  
  for (int i=0; ireset();
  m_vpp[1]->reset();
  m_rms[0]->reset();
  m_rms[1]->reset();
  m_freq[0]->reset();
  m_freq[1]->reset();
  
  update();
}

void
DsoWid::setTriggerStabilizerSLOT( bool on )
{
  m_triggerStabilizer = on;
}

void
DsoWid::setFftDrawModeSLOT( int channel, FFTDrawMode mode )
{
  m_fftDrawMode[channel] = mode;
}

void 
DsoWid::setTriggerSLOT( bool on )
{
  m_dso->setTriggerEnabled( on );
  resetEnvelopeSLOT();
}

void 
DsoWid::setTriggerRaisingSLOT( bool on )
{
  m_dso->setTriggerRaising( on );
  resetEnvelopeSLOT();
}

void 
DsoWid::setTriggerChannelSLOT( int channel )
{
  m_dso->setTriggerChannel( channel );
  resetEnvelopeSLOT();
}

void 
DsoWid::setTriggerExternalSLOT( bool ext )
{
  m_dso->setTriggerExternal( ext );
  resetEnvelopeSLOT();
}

void
DsoWid::setDcOffsetSLOT( int channel, int offset )
{
  m_dcOffset[channel] = offset;
  
  update();
}

void
DsoWid::setYOffsetSLOT( int channel, int offset )
{
  m_yOff[channel] = offset;
  
  update();
}

void
DsoWid::setInterpolSLOT( DsoWid::Interpolation type )
{
  m_interpolation = type;
  
  update();
}

void
DsoWid::setFftWindowNameSLOT( const QString & windowName )
{
  m_windowName = windowName;
  
  update();
}

void
DsoWid::setFftTypeSLOT( int type )
{
  m_fftType = (FFTType)type;
  flushFftAverageBufferSLOT();
}

void
DsoWid::setFftDisplaySLOT( int mode )
{
  m_fftDisplayMode = (FFTDisplayMode)mode;
  flushFftAverageBufferSLOT();
}

void
DsoWid::setFftShowPeaksSLOT( bool on )
{
  m_fftShowPeaks = on;
  
  update();
}

void
DsoWid::setFftZoomSLOT( int zoom )
{
  m_fftZoom = zoom;
  
  update();
}

void
DsoWid::setFftPostMagSLOT( float mag )
{
  m_fftPostMag = mag;
  flushFftAverageBufferSLOT();
  
  update();
}

void
DsoWid::setFftWindowSLOT( int id )
{
  m_fftWindow = id;
  flushFftAverageBufferSLOT();
}

void
DsoWid::setFftFreqSLOT( Dso::TimeBase tb )
{
  m_dso->setTimeBase( tb );  
  m_hasData = false;
  m_dso->resetNewData();
  
  setDsoAcqLength();
  
  flushFftAverageBufferSLOT();
}

void
DsoWid::setStepSLOT( int step )
{
  if (step >= 0)
  {
    createSinXInterpol();
  }
}

void
DsoWid::setStretchSLOT( int channel, float stretch )
{
  m_stretch[channel] = stretch;
  
  update();
}

void
DsoWid::setDrawTriggerLineSLOT( bool on )
{
  m_drawTriggerLine = on;
  
  update();
}

void
DsoWid::setShowChannel1SLOT( bool on )
{
  m_showChannel[0] = on;
  m_dso->setChannelEnable( 0, on );
  
  resetEnvelopeSLOT();
  update();
}

void
DsoWid::setShowChannel2SLOT( bool on )
{
  m_showChannel[1] = on;
  m_dso->setChannelEnable( 1, on );
  
  resetEnvelopeSLOT();
  update();
}

void
DsoWid::setMathModeSLOT( int mode )
{
  std::cerr << "SET MATH MODE SLOT " << mode << std::endl;
  
  m_mathMode = (MathMode)mode;

  switch (m_mathMode)
  {
  case NoMath:
    chColor[2] = addColor;
    break;
  case Add:
    chColor[2] = addColor;
    break;
  case Sub:
    chColor[2] = subColor;
    break;
  case Magnitude:
    chColor[2] = addColor;
    break;
  }
  
  applyPrefsSLOT();
  
  update();
}

void
DsoWid::setShowTimeMarkerSLOT( TimeMarkerMode mode )
{
  m_timeMarkerMode = mode;
  
  update();
}

void
DsoWid::setShowAmplitudeMarkerSLOT( bool on )
{
  m_showAmplitudeMarker = on;
  
  update();
}

void 
DsoWid::setXOffSLOT( int off )
{
  std::cerr << "xoff=" << off << std::endl;
  m_xOff = off;
  
  update();
}

void 
DsoWid::setMathOffSLOT( int off )
{
  m_yOff[2] = off;
  
  update();
}

void 
DsoWid::setMathStretchSLOT( float stretch )
{
  m_stretch[2] = stretch;
  
  update();
}

void 
DsoWid::setModeSLOT( DsoWid::Mode mode )
{
  m_mode = mode;
  
  if (m_mode != DsoWid::FFT)
  {
    setTimeBaseSLOT( m_tb[m_mode] );
  }
  else
  {
    setFftFreqSLOT( m_tb[m_mode] );    
  }
}

void
DsoWid::setDsoAcqLength()
{
  if (m_mode != DsoWid::FFT)
  {
    if (m_dso->fastAcq())
    {
      if (m_dso->step() < 0)
      {
        m_dso->setAcqLength( (int)((float)m_dso->numSamples() / 5. * 
            (float)(-m_dso->step())) );
        
        m_dso->setAcqOffset( m_dso->preTriggerSize() - (int)((float)m_dso->preTriggerSize() / 5. * 
            (float)(-m_dso->step())) );
      }
      else
      {
        m_dso->setAcqLength( (int)((float)m_dso->numSamples() / (float)m_dso->step() / 5.) );    
        
        m_dso->setAcqOffset( m_dso->preTriggerSize() - 
            (int)((float)m_dso->preTriggerSize() / (float)m_dso->step() / 5.) );
      }
    }
    else
    {
      m_dso->setAcqLength( m_dso->numSamples() );  
      m_dso->setAcqOffset( 0 );
    }
  }
  else
  {
    m_dso->setAcqLength( m_fftSize );
    m_dso->setAcqOffset( 0 );
  }
}

void
DsoWid::timerEvent( QTimerEvent *ev )
{
  static QTime t;
  static bool s_triggerOk = false;
  static int comeAgainCnt = 0;
  bool adjustFps = false;
  
  if (ev->timerId() == m_updateTimer || ev->timerId() == m_comeAgainTimer)
  {    
    if (m_comeAgainTimer != -1)
    {
      killTimer( m_comeAgainTimer );
      m_comeAgainTimer = -1;
    }
    
    if (adjustFps && m_continuousSampling)
    {
      adjustFps = false;
      killTimer( m_updateTimer );
      m_updateTimer = startTimer( 33 );
    }
    
    if (m_dso->hasNewData())
    {
      float rate = 1. / (float)t.elapsed() * 1000.;
      t.start();
      emit fps( rate );
      
      readFromDso();
      m_triggerOffset = m_triggerStabilizer ? m_dso->triggerOffset() : 0;
      m_dso->resetNewData();
      m_hasData = true;
      //std::cerr << "TR OFF: " << m_triggerOffset << std::endl;
      update();
      
      if (-1 == m_updateTimer && m_continuousSampling)
      {
        const int deltaT = QMAX( 0, 30-comeAgainCnt*10 );
        
        m_updateTimer = startTimer( deltaT );
        comeAgainCnt = 0;
        adjustFps = true;
      }
    }
    else
    {
      // COME BACK SOON! 
      // (TRY TO MAINTAIN MAX FRAMERATE, STILL BEEING COOPERATIVE)
      //
      m_comeAgainTimer = startTimer( 10 );
      comeAgainCnt++;
      if (-1 != m_updateTimer)
      {
        killTimer( m_updateTimer );
        m_updateTimer = -1;
      }
    }
    
    if (!m_dso->running())
    {
      m_dso->start();
    }
    
    if (s_triggerOk != m_dso->triggerOk())
    {
      emit triggerOk( m_dso->triggerOk() );
    }

    s_triggerOk = m_dso->triggerOk();  
  }
}

void
DsoWid::setVoltsDivSLOT( int channel, Dso::VoltsDiv id )
{  
  m_dso->setVoltsDiv( channel, id );
  
  m_vpp[0]->reset();
  m_vpp[1]->reset();
  m_rms[0]->reset();
  m_rms[1]->reset();
  
  resetEnvelopeSLOT();
  
  update();
}

void
DsoWid::setTimeBaseSLOT( Dso::TimeBase tb )
{
  m_tb[m_mode] = tb;
  m_dso->setTimeBase( tb );
  setStepSLOT( m_dso->step() );
  m_hasData = false;
  m_dso->resetNewData();
  
  setDsoAcqLength();
  
  resetEnvelopeSLOT();
  
  update();
}

void
DsoWid::resizeEvent( QResizeEvent * )
{
  m_buffer.resize( width(), height() );
}

void
DsoWid::setTriggerLevelSLOT( int triggerLevel )
{
  m_dso->setTriggerLevel( 255-triggerLevel );
  
  resetEnvelopeSLOT();
  
  update();
}

QImage
DsoWid::snapshot()
{
  return m_buffer.convertToImage();
}

void
DsoWid::paintEvent( QPaintEvent * )
{
  m_buffer.fill( bgColor );
  
  QPainter p;
  p.begin( &m_buffer );
  qDrawShadePanel( &p, 0, 0, width(), height(), 
                   colorGroup(), true, 5, 0 );
  p.end();
  paint( &m_buffer );  
  bitBlt( this, QPoint(0,0), &m_buffer, rect(), Qt::CopyROP );
}

void
DsoWid::paint( QPaintDevice *device, bool bw )
{
  QPainter p;
  p.begin( device );
  p.setFont( font() );
    
  // draw according to mode
  //
  switch (m_mode)
  {
  case DsoWid::DSO:
    drawEnvelope( &p, 0, bw );
    drawEnvelope( &p, 1, bw );
    drawEnvelope( &p, 2, bw );
    drawGrid( &p, bw );
    drawDSO( &p, bw );
    break;
  case DsoWid::XY:
    drawGrid( &p, bw );
    drawXY( &p, bw );
    break;
  case DsoWid::FFT:
    drawFFTGrid( &p, bw );
    drawFFT( &p, bw );
    break;
  default:   // oops
    p.end();
    return;
  }
    

  QString str;
  QString unit;
  
  if (DsoWid::DSO == m_mode || DsoWid::XY == m_mode)
  {
    if (m_dso->numSamples() == m_dso->acqLength())
    {
      bool  ok;

      for (int i=0; i<2; ++i)
      {
        const float f = frequency( i, &ok );

        if (ok)
        {
          m_freq[i]->addValue( f );
        }
        else
        {
          m_freq[i]->reset();
        }


        if (m_showChannel[i])
        {
          if (m_freq[i]->valid())
          {
            str = floatValueString( m_freq[i]->value(), Frequency, 4 );
          }
          else
          {
            str = "- Hz";
          }

          if (!bw)
          {
            p.setPen( chColor[i] );
          }
          
          if (0 == i)
          {
            p.drawText( m_x0+WINDOW_X_PIXELS-100-(m_showChannel[1] ? 100 : 0), 
                        5+2, 100, m_fh, 
                        Qt::AlignRight | Qt::AlignVCenter, str );
          }
          else
          {
            p.drawText( m_x0+WINDOW_X_PIXELS-100, 5+2, 100, m_fh, 
                        Qt::AlignRight | Qt::AlignVCenter, str );
          }
        }
      }
    }
  }
  else
  {
    // show string with frequency, windowtype, size, mode and averaging size
    //
    str = floatValueString( m_dso->samplingFrequency() / 2., Frequency );
    
    str += " ";
    str += m_windowName;    
    str += " (";
    str += QString().setNum(m_fftSize);
    str += tr(" points)");
    
    if (m_fftDisplayMode == Average)
    {
      str += tr(" Av. x");
      str += QString().setNum(m_fftOutAvCounter[0]);
    }
    else if (m_fftDisplayMode == Maximum)
    {
      str += " Maximum";
    }
    else if (m_fftDisplayMode == Minimum)
    {
      str += " Minimum";
    }
   
    if (!bw)
    {
      p.setPen( timeColor );
    }
    p.drawText( m_x0, 5+2, WINDOW_X_PIXELS, m_fh, 
                Qt::AlignRight | Qt::AlignVCenter, str );
  }
      
  p.end();
}

void
DsoWid::drawDSO( QPainter *p, bool bw )
{
  // draw in channel color
  //
  if (!bw)
  {
    p->setPen( QPen( chColor[m_dso->triggerChannel()], 0 ));
  }
  else
  {
    p->setPen( QPen( Qt::black, 1 ) );
  }
  
  // trigger position depending on channel y-pos and scaling
  //
  //int y = (int)qRound(m_y0+(WINDOW_Y_PIXELS)/2+m_yOff[m_dso->triggerChannel()]-
  //    ((m_dso->triggerLevel() & m_dso->triggerBits()))*m_stretch[m_dso->triggerChannel()]);
  const float stretch = m_stretch[m_dso->triggerChannel()] * m_dataStretch;
  int y = (WINDOW_Y_PIXELS)/2+m_y0+m_dcOffset[m_dso->triggerChannel()]*stretch+m_yOff[m_dso->triggerChannel()]
      -lrintf(stretch*(((m_dso->triggerLevel() & m_dso->triggerBits()))-m_dcOffset[m_dso->triggerChannel()])+m_dcOffset[m_dso->triggerChannel()]);
  
  
  // small triangles indicating level
  //
  p->moveTo( m_x0-7, y-2 );
  p->lineTo( m_x0-2, y );
  p->lineTo( m_x0-7, y+2 );
  p->lineTo( m_x0-7, y-2 );
  
  int x = m_x0+WINDOW_X_PIXELS+2;
  p->moveTo( x, y );
  p->lineTo( x+5, y-2 );
  p->lineTo( x+5, y+2 );
  p->lineTo( x, y );
  
  p->setClipRect( m_x0, m_y0, WINDOW_X_PIXELS, WINDOW_Y_PIXELS );
  
  // draw level line if slider is down
  //
  if (m_drawTriggerLine)
  {    
    p->setPen( QPen( chColor[m_dso->triggerChannel()], 0, QPen::DotLine ));
    p->drawLine( m_x0, y, m_x0+WINDOW_X_PIXELS, y );
  }
  
  if (!bw)
  {
    p->setPen( QPen( chColor[m_dso->triggerChannel()], 0 ));
  }
  else
  {
    p->setPen( QPen( Qt::black, 1 ));
  }
  
  x = m_x0+(m_dso->preTriggerSize()-m_xOff)/5;
  y = m_y0+WINDOW_Y_PIXELS-2;
  
  // small triangle indicating pretrigger prosition
  //
  p->moveTo( x-2, y );
  p->lineTo( x+2, y );
  p->lineTo( x, y-5 );
  p->lineTo( x-2, y );
    
  // draw position line if slider is down
  //
  if (m_drawTriggerLine)
  {
    if (!bw)
    {
      p->setPen( QPen( chColor[m_dso->triggerChannel()], 0, QPen::DotLine ));
    }
    else
    {
      p->setPen( QPen( Qt::black, 1, QPen::DotLine ));
    }
    
    p->drawLine( x, m_y0+5, x, m_y0+WINDOW_Y_PIXELS-5 );
  }
    
  p->setClipping( false );
  
  // draw all channels
  //
  doMath();

  drawChannel( p, 0, bw );
  drawChannel( p, 1, bw );    
  drawChannel( p, 2, bw );    

  // draw marker
  //
  drawTimeMarker( p, bw );
  drawAmplitudeMarker( p, bw );

  // draw info last. it might overlay something
  //
  drawDiv( p, 0, bw );
  drawDiv( p, 1, bw );
}

void
DsoWid::drawFFT( QPainter *p, bool bw )
{
  if (!m_hasData) return;
  
  const float fac = (float)(2*WINDOW_X_PIXELS)/fftSize()*(float)m_fftZoom;
  const int numPoints = fftSize()/2/m_fftZoom;
  
  // channel 0 is on top of channel 1
  //
  for (int channel=1; channel>=0; --channel)
  {
    if (m_showChannel[channel])
    {
      fft( channel );

      if (!bw)
      {
        p->setPen( QPen( chColor[channel], s_lineWidthFft ));
      }
      else
      {
        p->setPen( QPen( Qt::black, s_lineWidth ));
      }

      p->setClipRect( m_x0, m_y0, WINDOW_X_PIXELS, WINDOW_Y_PIXELS );

      int yOff = WINDOW_Y_PIXELS-(int)m_maxFftVal;
      
      if (m_fftDrawMode[channel] != Needles)
      {
        if (Lin == m_fftGridMode)
        {
          for (int i=0; isetPoint( i, 
                              static_cast (m_x0+fac*(float)i), 
                              static_cast (m_y0+WINDOW_Y_PIXELS-m_fftOut[i+m_xOff]-yOff ));
          }
        }
        else
        {
          const float fStep = m_dso->samplingFrequency() / (float)m_fftSize;
          const float lFac = (float)WINDOW_X_PIXELS / log10( m_dso->samplingFrequency() / 2. ) * (float)m_fftZoom;
          const int xOff = lrintf( pow( 10, (m_xOff/lFac)) / fStep );
          
          for (int i=0; i (m_x0+lrintf( lFac*log10((float)i*fStep) ));
            
            m_arr3->setPoint( i, x, static_cast (m_y0+WINDOW_Y_PIXELS-m_fftOut[i+xOff]-yOff ));
          }
        }
        
        if (m_fftDrawMode[channel] == Line)
        {
          p->drawPolyline( *m_arr3, 0, numPoints );
        }
        else
        {
          p->drawPoints( *m_arr3, 0, numPoints );
          m_arr3->translate( 0, 1 );
          p->drawPoints( *m_arr3, 0, numPoints );
        }
      }
      else
      {
        if (Lin == m_fftGridMode)
        {
          if (m_fftType == Power)
          {
            for (int i=0; isetPoint( 2*i, static_cast (m_x0+fac*(float)i), 
                                     m_y0+WINDOW_Y_PIXELS );
              m_arr3->setPoint( 2*i+1, static_cast (m_x0+fac*(float)i), 
                                       static_cast (m_y0+WINDOW_Y_PIXELS-m_fftOut[i+m_xOff] ));
            }
          }
          else
          {
            for (int i=0; isetPoint( 2*i, static_cast (m_x0+fac*(float)i), 
                                     m_y0+WINDOW_Y_PIXELS/2 );
              m_arr3->setPoint( 2*i+1, static_cast (m_x0+fac*(float)i), 
                                       static_cast (m_y0+WINDOW_Y_PIXELS-m_fftOut[i+m_xOff] ));
            }
          }
        }
        else
        {
          const float fStep = m_dso->samplingFrequency() / (float)m_fftSize;
          const float lFac = (float)WINDOW_X_PIXELS / log10( m_dso->samplingFrequency() / 2. ) * (float)m_fftZoom;
          
          if (m_fftType == Power)
          {
            for (int i=0; isetPoint( 2*i, x, m_y0+WINDOW_Y_PIXELS );
              m_arr3->setPoint( 2*i+1, x, 
                                static_cast (m_y0+WINDOW_Y_PIXELS-m_fftOut[i+m_xOff] ));
            }
          }
          else
          {
            for (int i=0; isetPoint( 2*i, x, m_y0+WINDOW_Y_PIXELS/2 );
              m_arr3->setPoint( 2*i+1, x, 
                                static_cast (m_y0+WINDOW_Y_PIXELS-m_fftOut[i+m_xOff] ));
            }
          }
        }
        
        p->drawLineSegments( *m_arr3, 0, numPoints );
      }

      if (m_fftShowPeaks && m_fftType == Power)
      {
        p->setPen( QPen( timeColor, 0 ));
        showFftPeaks( p, fac );
      }
      p->setClipping( false );
    }
  }
  
  drawFreqMarker( p, bw );
  drawDbMarker( p, bw );
}

void
DsoWid::drawXY( QPainter *p, bool bw )
{
  int xOff = (WINDOW_X_PIXELS-256)/2;
  const float stretchX = m_stretch[0] * m_dataStretch;
  const float stretchY = m_stretch[1] * m_dataStretch;
  
  if (!bw)
  {
    p->setPen( QPen( chColor[0], s_lineWidth ));
  }
  else
  {
    p->setPen( QPen( Qt::black, s_lineWidth ));
  }
  
  
  for (int i=0; iacqLength(); ++i)
  {
    m_arr2->setPoint( i, m_x0+xOff+(int)(stretchX*m_data[0][i])-m_yOff[0], 
                        WINDOW_Y_PIXELS-64-(int)(stretchY*m_data[1][i])+m_y0+m_yOff[1] );
  }
  
  p->setClipRect( m_x0, m_y0, WINDOW_X_PIXELS, WINDOW_Y_PIXELS );
  p->drawPolyline( *m_arr2, 0, m_dso->acqLength() );
  p->setClipping( false );
  
  drawDiv( p, 0, bw );
  drawDiv( p, 1, bw );  
}

void
DsoWid::drawTimeMarker( QPainter *p, bool bw )
{
  if (Off == m_timeMarkerMode) return;
  
  if (!bw)
  {
    p->setPen( QPen( timeColor, 0, QPen::DotLine ));
    //p->setRasterOp( XorROP );
  }
  else
  {
    p->setPen( QPen( Qt::black, 1, QPen::DotLine ));
  }
    
  p->drawLine( m_x0+m_timeMarker[0], m_y0, m_x0+m_timeMarker[0], m_y0+WINDOW_Y_PIXELS );
  if (RatioMarker == m_timeMarkerMode)
  {
    p->drawLine( m_x0+m_timeMarker[1], m_y0, m_x0+m_timeMarker[1], m_y0+WINDOW_Y_PIXELS );
  }
  p->drawLine( m_x0+m_timeMarker[2], m_y0, m_x0+m_timeMarker[2], m_y0+WINDOW_Y_PIXELS );
  
  p->setRasterOp( CopyROP );
  
  float diff[3];
  diff[0] = fabs( m_timeMarker[0]-m_timeMarker[1] );
  diff[1] = fabs( m_timeMarker[1]-m_timeMarker[2] );
  diff[2] = fabs( m_timeMarker[0]-m_timeMarker[2] );
  
  float dt;
  float pv;
  
  // sort for the user (hardcoded bubblesort)
  //
  if (m_timeMarker[0] > m_timeMarker[1])
  {
    if (m_timeMarker[0] > m_timeMarker[2])
    {
      if (m_timeMarker[1] > m_timeMarker[2])
      {
        // 2 1 0
        dt = diff[2];
        if (diff[0] != 0)
        {
          pv = diff[1] / diff[0];
        }
        else
        {
          pv = 0;
        }
      }
      else
      {
        // 1 2 0
        dt = diff[0];
        if (diff[2] != 0)
        {
          pv = diff[1] / diff[2];
        }
        else
        {
          pv = 0;
        }
      }
    }
    else
    {
      // 1 0 2
      dt = diff[1];
      if (diff[2] != 0)
      {
        pv = diff[0] / diff[2];
      }
      else
      {
        pv = 0;
      }
    }
  }
  else
  {
    if (m_timeMarker[0] > m_timeMarker[2])
    {
      // 2 0 1
      dt = diff[1];
      if (diff[0] != 0)
      {
        pv = diff[2] / diff[0];
      }
      else
      {
        pv = 0;
      }
    }
    else
    {
      if (m_timeMarker[1] > m_timeMarker[2])
      {
        // 0 2 1
        dt = diff[0];
        if (diff[1] != 0)
        {
          pv = diff[2] / diff[1];
        }
        else
        {
          pv = 0;
        }
      }
      else
      {
        // 0 1 2
        dt = diff[2];
        if (diff[1] != 0)
        {
          pv = diff[0] / diff[1];
        }
        else
        {
          pv = 0;
        }
      }
    }
  }
  
  dt = dt / 32. * m_dso->timeBase();
  
  if (dt != 0.0)
  {
    float f = 1./dt;
    
    QString str = "dt=";
    str += floatValueString( dt, Time );
    str += "  f=";
    str += floatValueString( f, Frequency );
    
    if (RatioMarker == m_timeMarkerMode)
    {
      str += "  ";
      str += floatValueString( pv, Ratio, 2 );
    }
    
    p->drawText( m_x0, m_y0+WINDOW_Y_PIXELS+2, WINDOW_X_PIXELS, m_fh, 
                 Qt::AlignLeft | Qt::AlignVCenter, str );
  }
}

void
DsoWid::drawFreqMarker( QPainter *p, bool bw )
{
  const float fac = (float)WINDOW_X_PIXELS/fftSize()*2.*(float)m_fftZoom;
  
  if (Off == m_timeMarkerMode) return;
    
  if (!bw)
  {
    p->setPen( QPen( timeColor, 0, QPen::DotLine ));
  }
  else
  {
    p->setPen( QPen( Qt::black, 1, QPen::DotLine ));
  }
    
  p->drawLine( m_x0+m_freqMarker, m_y0, m_x0+m_freqMarker, m_y0+WINDOW_Y_PIXELS );
  
  float f = ( m_xOff * fac + m_freqMarker ) / (float)WINDOW_X_PIXELS * (m_dso->samplingFrequency() / 2.) / m_fftZoom;
  
  QString str = "f=";
  str += floatValueString( f, Frequency );

  p->drawText( m_x0, m_y0-m_fh, WINDOW_X_PIXELS, m_fh, Qt::AlignLeft | Qt::AlignVCenter,
               str );
}

void 
DsoWid::drawAmplitudeMarker( QPainter *p, bool bw )
{
  if (!m_showAmplitudeMarker) return;
  
  int channel = 0;
  
  if (m_showChannel[0])
  {
    channel = 0;
  }
  else
  {
    channel = 1;
    
  }
  
  if (!bw)
  {
    p->setPen( QPen( chColor[channel], 0, QPen::DotLine ));
  }
  else
  {
    p->setPen( QPen( Qt::black, 1, QPen::DotLine ));
  }
  p->drawLine( m_x0, m_y0+m_amplitudeMarker[0], m_x0+WINDOW_X_PIXELS, m_y0+m_amplitudeMarker[0] );
  p->drawLine( m_x0, m_y0+m_amplitudeMarker[1], m_x0+WINDOW_X_PIXELS, m_y0+m_amplitudeMarker[1] );
  
  float dv = volts( channel, fabs( m_amplitudeMarker[0]-m_amplitudeMarker[1] ) ) / m_stretch[channel] * m_probe[channel];
  
  if (dv != 0.0)
  {
    QString str = "dV=";
    str += floatValueString( dv, Voltage );
    
    p->drawText( m_x0, m_y0+WINDOW_Y_PIXELS+2, WINDOW_X_PIXELS, m_fh, 
                 Qt::AlignRight | Qt::AlignVCenter, str );
  }
}

void 
DsoWid::drawDbMarker( QPainter *p, bool bw )
{
  if (!m_showAmplitudeMarker) return;
  
  if (m_showChannel[0])
  {
    if (!bw)
    {
      p->setPen( QPen( chColor[0], 0, QPen::DotLine ));
    }
    else
    {
      p->setPen( QPen( Qt::black, 1, QPen::DotLine ));
    }
  }
  else
  {
    if (!bw)
    {
      p->setPen( QPen( chColor[1], 0, QPen::DotLine ));
    }
    else
    {
      p->setPen( QPen( Qt::black, 1, QPen::DotLine ));
    }
  }
  
  p->drawLine( m_x0, m_y0+m_dbMarker[0], m_x0+WINDOW_X_PIXELS, m_y0+m_dbMarker[0] );
  p->drawLine( m_x0, m_y0+m_dbMarker[1], m_x0+WINDOW_X_PIXELS, m_y0+m_dbMarker[1] );
  
  if (Power == m_fftType)
  {
    float dv = fabs( m_dbMarker[0]-m_dbMarker[1] ) / 64. * 10.;

    if (dv != 0.0)
    {
      QString str; 
      str.sprintf( "d=%.2fdB", (float)dv );
      p->drawText( m_x0+100, m_y0-m_fh, 240, m_fh, 
                   Qt::AlignLeft | Qt::AlignVCenter, str );
    }
  }
  else
  {
    float dv = fabs( m_dbMarker[0]-m_dbMarker[1] ) / (float)WINDOW_Y_PIXELS * 360.;

    if (dv != 0.0)
    {
      QString str; 
      str.sprintf( "d=%.2f", (float)dv );
      p->drawText( m_x0+100, m_y0-m_fh, 240, m_fh, 
                   Qt::AlignLeft | Qt::AlignVCenter, str );
    }
  }
}

void
DsoWid::drawGrid( QPainter *p, bool bw )
{
  if (!bw)
  {
    p->setPen( gridColor );
  }
  else
  {
    p->setPen( QPen( Qt::black, 0 ) );
  }
  
  int verSteps = WINDOW_Y_PIXELS / 32;
  int horSteps = WINDOW_X_PIXELS / 32;
  
  // horizontal lines
  //
  for (int y=1; ydrawLine( m_x0, m_y0+32*y, m_x0+WINDOW_X_PIXELS-1, m_y0+32*y );
  }
  
  for (int x=1; xdrawLine( m_x0+32*x, m_y0, m_x0+32*x, m_y0+WINDOW_Y_PIXELS-1 );
  }
  
  float step = 6.4f;
  float pos = m_x0+6.4f;
  
  int xCenter = WINDOW_X_PIXELS / 2;
  int yCenter = WINDOW_Y_PIXELS / 2;
  int numXTicks = lrint((float)WINDOW_X_PIXELS / 6.4f);
  int numYTicks = lrint((float)WINDOW_Y_PIXELS / 6.4f);
  
  // center line grid
  //
  for (int x=1; xdrawLine( lrintf(pos), m_y0+yCenter-2, lrintf(pos), m_y0+yCenter+2 );
    
    pos += step;
  }
  
  pos = m_y0+6.4;
  
  for (int y=1; ydrawLine( m_x0+xCenter-2, xPos, 
                 m_x0+xCenter+2, xPos );
    
    pos += step;
  }
        
  if (!bw)
  {
    p->setPen( QPen( gridColor, 0, QPen::DotLine ) );
  }
  else
  {
    p->setPen( QPen( Qt::black, 0, QPen::DotLine ) );
  }
  
  p->drawLine( m_x0, m_y0+yCenter-80, 
               m_x0+WINDOW_X_PIXELS, m_y0+yCenter-80 );
  p->drawLine( m_x0, m_y0+yCenter+80, 
               m_x0+WINDOW_X_PIXELS, m_y0+yCenter+80 );
  
  if (!bw)
  {
    p->setPen( borderColor );
  }
  else
  {
    p->setPen( QPen( Qt::black, 0 ) );
  }
  
  p->drawText( 6, m_y0+yCenter-80-m_fh/2, m_legWidth-8, m_fh, 
               Qt::AlignVCenter | Qt::AlignRight, "100%" );
  p->drawText( 6, m_y0+yCenter-64-m_fh/2, m_legWidth-8, m_fh, 
               Qt::AlignVCenter | Qt::AlignRight, "90%" );
  p->drawText( 6, m_y0+yCenter+64-m_fh/2, m_legWidth-8, m_fh, 
               Qt::AlignVCenter | Qt::AlignRight, "10%" );
  p->drawText( 6, m_y0+yCenter+80-m_fh/2, m_legWidth-8, m_fh, 
               Qt::AlignVCenter | Qt::AlignRight, "0%" );
  
  p->drawRect( m_x0-1, m_y0-1, WINDOW_X_PIXELS+2, WINDOW_Y_PIXELS+2 );
  
}

void
DsoWid::drawFFTGrid( QPainter *p, bool bw )
{
  if (!bw)
  {
    p->setPen( borderColor );
  }
  else
  {
    p->setPen( QPen( Qt::black, 0 )  );
  }
  
  QString str;
  QString unit;
  
  // horizontal lines
  //
  if (Power == m_fftType)
  {
    p->drawText( 8, 8+m_fh, "[dbV]" );

    for (int y=1; y<16; ++y)
    {
      if (!bw)
      {
        p->setPen( gridColor );
      }
      p->drawLine( m_x0, m_y0+y*24, m_x0+WINDOW_X_PIXELS, m_y0+y*24 );

      if (y & 1)
      {
        str.sprintf( "-%d", y*5 );
        if (!bw)
        {
          p->setPen( borderColor );
        }
        p->drawText( 8, m_y0+y*24-m_fh/2, m_x0-12, m_fh, 
                     Qt::AlignRight | Qt::AlignVCenter, str );
      }                 
    }
  }
  else
  {
    for (int y=0; y<=8; ++y)
    {
      if (!bw)
      {
        p->setPen( gridColor );
      }
      p->drawLine( m_x0, m_y0+y*48, m_x0+WINDOW_X_PIXELS, m_y0+y*48 );

      if (y & 1)
      {
        str.sprintf( "%d", 180-45*y );
        if (!bw)
        {
          p->setPen( borderColor );
        }
        p->drawText( 8, m_y0+y*48-m_fh/2, m_x0-12, m_fh, 
                     Qt::AlignRight | Qt::AlignVCenter, str );
        
        if (4 == y)
        {
          p->drawLine( m_x0, m_y0+y*48, m_x0+WINDOW_X_PIXELS, m_y0+y*48 );
        }
      }                 
    }    
  }
  
  if (Lin == m_fftGridMode)
  {
    // vertical lines
    //
    float freqStep = computeFreqScaleStep();
    const float fac = (float)WINDOW_X_PIXELS/(fftSize()-1)*2.*(float)m_fftZoom;
    float step = ((float)WINDOW_X_PIXELS/(m_dso->samplingFrequency() / 2.)*(float)m_fftZoom*freqStep);
    //step /= 2.;
    int numLines = (int)( WINDOW_X_PIXELS / step + 1 );

    float startFreq = (int)(fac*m_xOff/step)*freqStep;

    for (int i=0; i m_x0 && pos < m_x0+WINDOW_X_PIXELS)
      {
        if (!bw)
        {
          p->setPen( gridColor );
        }
        p->drawLine( pos, m_y0, pos, m_y0+WINDOW_Y_PIXELS );

        if (pos < m_x0+WINDOW_X_PIXELS-m_tw/2)
        {
          str = floatValueString( startFreq, Frequency );

          if (!bw)
          {
            p->setPen( borderColor );
          }
          p->drawText( pos-m_tw/2, m_y0+WINDOW_Y_PIXELS, m_tw, m_fh, 
                       Qt::AlignCenter, str );
        }
      }
      startFreq += freqStep;
    }
  }
  else
  {
    // vertical lines
    //
    float fac = 1;
    float step = ((float)WINDOW_X_PIXELS/log10(m_dso->samplingFrequency() / 2.)*(float)m_fftZoom);
    //step /= 2.;
    int numLines = (int)( WINDOW_X_PIXELS / step + 1 );

    float startFreq = (int)(m_xOff/step);

    for (int i=0; isetPen( gridColor );
      }
      for (int j=1; j<10; ++j)
      {
        int sp = pos + lrintf( step*log10((float)j));
        if (sp > m_x0 && sp < m_x0+WINDOW_X_PIXELS)
        {
          p->drawLine( sp, m_y0, sp, m_y0+WINDOW_Y_PIXELS );
        }
      }
      
      if (pos > m_x0 && pos < m_x0+WINDOW_X_PIXELS)
      {
        if (!bw)
        {
          p->setPen( gridColor );
        }
        p->drawLine( pos, m_y0, pos, m_y0+WINDOW_Y_PIXELS );

        if (pos < m_x0+WINDOW_X_PIXELS-m_tw/2)
        {
          str = floatValueString( pow( 10, startFreq ), Frequency );

          if (!bw)
          {
            p->setPen( borderColor );
          }
          p->drawText( pos-m_tw/2, m_y0+WINDOW_Y_PIXELS, m_tw, m_fh, 
                       Qt::AlignCenter, str );
        }
      }
      startFreq ++;
    }
  }
  
  // draw border
  //
  if (!bw)
  {
    p->setPen( borderColor );
  }
  
  p->drawRect( m_x0-1, m_y0-1, WINDOW_X_PIXELS+2, WINDOW_Y_PIXELS+2 );
}

float
DsoWid::computeFreqScaleStep()
{
  float winWidth = (m_dso->samplingFrequency() / 2.) / m_fftZoom;  
  float winStep = winWidth / (float)WINDOW_X_PIXELS * (float)m_tw;    
  float step = 1.;
  
  while (step < 1e8)
  {
    if (winStep > step && winStep <= 2.*step)
    {
      return 2.*step;
    }
    else if (winStep > 2.*step && winStep <= 5.*step)
    {
      return 5.*step;
    }
    else if (winStep > 5.*step && winStep <= 10.*step)
    {
      return 10.*step;
    }
    
    step *= 10.;
  }
  
  return 1e6;
}

void
DsoWid::drawEnvelope( QPainter *p, int channel, bool bw )
{
  if (!m_showChannel[channel]) return;
  
  const int lineOffset = s_lineWidth >> 1; 
  const float stretch = m_stretch[channel] * m_dataStretch;
  const int yOff = (WINDOW_Y_PIXELS)/2+m_y0+1+m_yOff[channel];
  
  p->setClipRect( m_x0, m_y0, WINDOW_X_PIXELS, WINDOW_Y_PIXELS );
  
  // show envelope
  //
  if (m_showEnvelope)
  {
    if (m_dso->step() < 0)      // oversampling
    {  
      int xOff = lrintf( (float)m_xOff / 5. * (float)(-m_dso->step()) + 
                 1024. + 204.8 * (float)m_dso->step() );
    
      QPointArray env( 2*m_dso->numSamples() );
      int numPoints = 0;
      
      for (int i=0; istep()*i+xOff;
        
        for (int j=0; j<-m_dso->step(); ++j)
        {  
          env.setPoint( numPoints, m_x0+i, 
                        yOff-lrintf(stretch *
                          (m_envelopeMin[channel][dataIndex]-m_dcOffset[channel])+m_dcOffset[channel]-lineOffset));
          env.setPoint( numPoints+1, m_x0+i, 
                        yOff-lrintf(stretch *
                          (m_envelopeMax[channel][dataIndex]-m_dcOffset[channel])+m_dcOffset[channel]+lineOffset));

          dataIndex++;
          numPoints += 2;
        }
      }
      if (bw)
      {
        p->setPen( QPen( Qt::lightGray, 1 ) );
      }
      else
      {
        p->setPen( chColor[channel].dark() );
      }
      
      p->drawLineSegments( env, 0, numPoints );
    } 
  }  
  
  p->setClipping( false );
}

void
DsoWid::drawChannel( QPainter *p, int channel, bool bw )
{
  if (!m_showChannel[channel]) return;
  
  const float stretch = m_stretch[channel] * m_dataStretch;
  
  p->setClipRect( m_x0, m_y0, WINDOW_X_PIXELS, WINDOW_Y_PIXELS );

  // 0 > step means (-step)x averaging (-step measurements per pixel)
  // otherwise step means step in pixel per measurement value
  //
  const int yOff = (WINDOW_Y_PIXELS)/2+m_y0+m_dcOffset[channel]*stretch+m_yOff[channel];
  
  if (m_dso->step() < 0)      // oversampling
  {  
    int xOff = lrintf( (float)m_xOff / 5. * (float)(-m_dso->step()) + 
        1024. + 204.8 * (float)m_dso->step() );
      
    if (Dots == m_interpolation)                    // no interpolation
    {
      // fill array
      //
      for (int i=0; istep(); ++j)
        {  
          const float val = mathSample( channel, 
                                        -m_dso->step()*i+j+xOff ) + m_dcOffset[channel];

          m_arr->setPoint( -m_dso->step()*i+j, m_x0+i, 
                            yOff-lrintf(stretch*(val-m_dcOffset[channel])+m_dcOffset[channel]) );
        }
      }
      drawPoints( p, channel, (-m_dso->step())*WINDOW_X_PIXELS, bw );
    }
    else if (LinearAverage == m_interpolation)      // linear average
    {
      // fill array
      //
      for (int i=0; istep(); ++j)
        {
          sum += mathSample( channel, -m_dso->step()*i+j+xOff ) + m_dcOffset[channel];
        }
        sum /= (float)(-m_dso->step());

        m_arr->setPoint( i, m_x0+i, yOff-lrintf(stretch*(sum-m_dcOffset[channel])+m_dcOffset[channel]) );
      }
      
      drawLine( p, channel, WINDOW_X_PIXELS, bw );
    }
    else                                            // linear
    {
      // fill array
      //
      for (int i=0; istep(); ++j)
        {  
          const float val = mathSample( channel, -m_dso->step()*i+j+xOff ) + m_dcOffset[channel];

          m_arr->setPoint( -m_dso->step()*i+j, m_x0+i, yOff-lrintf(stretch*(val-m_dcOffset[channel])+m_dcOffset[channel]) );
        }
      }
      
      drawLine( p, channel, (-m_dso->step())*WINDOW_X_PIXELS, bw );
    }
  }
  else              // undersampling
  {
    int xOff = lrintf( ((float)m_xOff-1024.) / 5. / (float)m_dso->step() + 1024. );
    
    if (SinX == m_interpolation)                    // sin(x)/x interpolation
    {
      // fill filter
      //
      int off = 0;
      xOff -= m_sinXLength/m_dso->step()+1;
      
      for (int i=0; istep()+1; ++i, ++off)
      {
        float val = (float)(mathSample( channel, i+xOff ) + m_dcOffset[channel]) / (float)SAMPLE_MAXVAL;

        for (int j=0; jstep(); ++j)
        {
          (void)sinXFilter(val);
        }
      }
        
      // fill array
      //
      for (int i=0; istep()+1; ++i)
      {
        float val = (float)( mathSample( channel, i+xOff+off ) + m_dcOffset[channel]) / (float)SAMPLE_MAXVAL;

        for (int j=0; jstep(); ++j)
        {
          m_arr->setPoint( i*m_dso->step()+j, m_x0+i*m_dso->step()+j, 
                           yOff-lrintf(stretch*(sinXFilter(val)*(float)SAMPLE_MAXVAL-m_dcOffset[channel])+
                              m_dcOffset[channel]) );
        }
      }
      
      drawLine( p, channel, WINDOW_X_PIXELS, bw );      
    }
    else                                          // linear or no interpolation
    {
      // fill array
      //
      int start = m_triggerOffset/m_dso->step();
      for (int i=0; istep(); ++i)
      {
        float val = (float)mathSample( channel, i+xOff+start ) + m_dcOffset[channel];

        m_arr->setPoint( i, m_x0+i*m_dso->step()-m_triggerOffset, yOff-lrintf(stretch*(val-m_dcOffset[channel])+m_dcOffset[channel]) );
      }
      
      if (Dots == m_interpolation)
      {
        drawPoints( p, channel, WINDOW_X_PIXELS/m_dso->step(), bw );    
      }
      else
      {
        drawLine( p, channel, WINDOW_X_PIXELS/m_dso->step(), bw );
      }
    }
  }
  
  p->setClipping( false );
}

void
DsoWid::drawLine( QPainter *p, int channel, int numPoints, bool print ) const
{
  p->save();
  
  int lw = s_lineWidth-1;
  const int lineOffset = s_lineWidth >> 1;
  
  if (print)
  {
    p->setPen( QPen( Qt::black, 1 ) );    
    p->translate( 0, -lineOffset );

    do
    {
      p->translate( 0, 1 );
      p->drawPolyline( *m_arr, 0, numPoints );
    }
    while (lw--);
  }
  else
  { 
    if (m_invCh[channel] || m_absCh[channel])
    {
      p->setPen( QPen( chChangedColor[channel], 0 ) );
    }
    else
    {
      p->setPen( QPen( chColor[channel], 0 ) );
    }
    
    p->translate( 0, -lineOffset );

    do
    {       
      p->translate( 0, 1 );
      p->drawPolyline( *m_arr, 0, numPoints );
    }
    while (lw--);
  }
  
  p->restore();
}

void
DsoWid::drawPoints( QPainter *p, int channel, int numPoints, bool /*print*/ ) const
{
  p->save();
  
  int lw = s_lineWidth-1;
  const int lineOffset = s_lineWidth >> 1;
  //const int lineWidth = print ? 1 : 0;
  
  if (lw > 0)
  {
    if (m_invCh[channel] || m_absCh[channel])
    {
      p->setPen( QPen( chChangedColor[channel], 0 ) );
    }
    else
    {
      p->setPen( QPen( chColor[channel], 0 ) );
    }
    p->translate( 0, -lineOffset );

    do
    {
      p->translate( 0, 1 );
      p->drawPoints( *m_arr, 0, numPoints );
    }
    while (lw--);
  }
 
  p->restore();  
}

void
DsoWid::drawDiv( QPainter *p, int channel, bool bw )
{  
  if (!m_showChannel[channel]) return;
  
  if (!bw)
  {
    p->setPen( QPen( chColor[channel], s_lineWidth ) );
  }
  else
  {
    p->setPen( QPen( Qt::black, s_lineWidth ));
  }
  
  float voltsDiv = m_dso->voltsDiv( channel ) / m_stretch[channel] * m_probe[channel];

  QString str = floatValueString( voltsDiv, Voltage );
  str += tr( " / div" );
  
  p->drawText( m_x0+100*channel, 5+2, 200, m_fh, Qt::AlignLeft | Qt::AlignVCenter,
               str );
  
  // draw measured voltages
  //
  if (m_drawMeasuredVolts && m_dso->numSamples() == m_dso->acqLength())
  {
    if (m_vpp[channel]->valid() && m_rms[channel]->valid())
    {
      str = floatValueString( m_vpp[channel]->value(), Voltage );
      str += "pp / ";
      str += floatValueString( m_rms[channel]->value(), Voltage );
      str += "rms";
    
      if (0 == channel)
      {
        const int w = fontMetrics().width( str );
        if (!bw)
        {
          p->fillRect( m_x0+1, m_y0+1, w, m_fh, bgColor );
        }
        else
        {
          p->fillRect( m_x0+1, m_y0+1, w, m_fh, Qt::white );
        }
        p->drawText( m_x0+1, 
                     m_y0+1, w, m_fh, Qt::AlignLeft | Qt::AlignVCenter,
                     str );
      }
      else
      {
        const int w = fontMetrics().width( str );
        if (!bw)
        {
          p->fillRect( m_x0+WINDOW_X_PIXELS-w-1, m_y0+1, w, m_fh, bgColor );
        }
        else
        {
          p->fillRect( m_x0+WINDOW_X_PIXELS-w-1, m_y0+1, w, m_fh, Qt::white );
        }
        p->drawText( m_x0+WINDOW_X_PIXELS-w-1, 
                     m_y0+1, w, m_fh, Qt::AlignRight | Qt::AlignVCenter,
                     str );
      }
    }
  }
  
  // draw time division
  //
  str = floatValueString( m_dso->timeBase(), Time );
  str += " / div";
  
  p->setPen( timeColor );
  p->drawText( m_x0+200, 5+2, 200, m_fh, Qt::AlignLeft | Qt::AlignVCenter, str );
}

void
DsoWid::doMath()
{
  const int acqPlus = m_dso->acqOffset() + m_dso->acqLength();
  
  switch (m_mathMode)
  {
  case NoMath:
    m_showChannel[2] = false;
    break;    
  case Add:
    m_showChannel[2] = true;
    
    for (int i=m_dso->acqOffset(); iacqOffset(); iacqOffset(); ix()-m_x0;
  int y = ev->y()-m_y0;
  
  if (m_mouseButton == LeftButton)
  {
    int dx = 0;
    int dy = 0;

    switch (m_moving)
    {
    case Freq:
      dx = m_freqMarker - x;
      m_freqMarker -= dx;
      if (m_freqMarker < 1) m_freqMarker = 1;
      if (m_freqMarker > WINDOW_X_PIXELS-1) m_freqMarker = WINDOW_X_PIXELS-1;
      break;
    case Time1:
      dx = m_timeMarker[0] - x;
      m_timeMarker[0] -= dx;
      if (m_timeMarker[0] < 1) m_timeMarker[0] = 1;
      if (m_timeMarker[0] > WINDOW_X_PIXELS-1) m_timeMarker[0] = WINDOW_X_PIXELS-1;
      if (TimeMarker == m_timeMarkerMode)
      {
        m_timeMarker[1] = QMIN( m_timeMarker[0], m_timeMarker[2] ) + 
            (QMAX( m_timeMarker[0], m_timeMarker[2] ) - QMIN( m_timeMarker[0], m_timeMarker[2] ))/2;  
      }
      break;
    case Time2:
      dx = m_timeMarker[1] - x;
      m_timeMarker[1] -= dx;
      if (m_timeMarker[1] < 1) m_timeMarker[1] = 1;
      if (m_timeMarker[1] > WINDOW_X_PIXELS-1) m_timeMarker[1] = WINDOW_X_PIXELS-1;
      break;
    case Time3:
      dx = m_timeMarker[2] - x;
      m_timeMarker[2] -= dx;
      if (m_timeMarker[2] < 1) m_timeMarker[2] = 1;
      if (m_timeMarker[2] > WINDOW_X_PIXELS-1) m_timeMarker[2] = WINDOW_X_PIXELS-1;
      if (TimeMarker == m_timeMarkerMode)
      {
        m_timeMarker[1] = QMIN( m_timeMarker[0], m_timeMarker[2] ) + 
            (QMAX( m_timeMarker[0], m_timeMarker[2] ) - QMIN( m_timeMarker[0], m_timeMarker[2] ))/2;  
      }
      break;
    case Amplitude1:
      dy = m_amplitudeMarker[0] - y;
      m_amplitudeMarker[0] -= dy;
      if (m_amplitudeMarker[0] < 1) m_amplitudeMarker[0] = 1;
      if (m_amplitudeMarker[0] > WINDOW_Y_PIXELS-1) m_amplitudeMarker[0] = WINDOW_Y_PIXELS-1;
      break;
    case Amplitude2:
      dy = m_amplitudeMarker[1] - y;
      m_amplitudeMarker[1] -= dy;
      if (m_amplitudeMarker[1] < 1) m_amplitudeMarker[1] = 1;
      if (m_amplitudeMarker[1] > WINDOW_Y_PIXELS-1) m_amplitudeMarker[1] = WINDOW_Y_PIXELS-1;
      break;
    case Db1:
      dy = m_dbMarker[0] - y;
      m_dbMarker[0] -= dy;
      if (m_dbMarker[0] < 1) m_dbMarker[0] = 1;
      if (m_dbMarker[0] > WINDOW_Y_PIXELS-1) m_dbMarker[0] = WINDOW_Y_PIXELS-1;
      break;
    case Db2:
      dy = m_dbMarker[1] - y;
      m_dbMarker[1] -= dy;
      if (m_dbMarker[1] < 1) m_dbMarker[1] = 1;
      if (m_dbMarker[1] > WINDOW_Y_PIXELS-1) m_dbMarker[1] = WINDOW_Y_PIXELS-1;
      break;
    default:
      if (m_mode == FFT)
      {
        dx = abs(m_x - ev->x() + m_x0);
        dy = abs(m_y - ev->y() + m_y0);

        if (dx > 3 || dy > 3)
        {
          if (dx > dy && abs(dx-dy) > 3 && m_timeMarkerMode != Off)
          {
            m_moving = Freq;
            m_freqMarker = ev->x()-m_x0;
          }
        }
      }
      break;
    }

    update();
  }
}

void
DsoWid::mousePressEvent( QMouseEvent *ev )
{
  m_x = ev->x()-m_x0;
  m_y = ev->y()-m_y0;
  
  m_mouseButton = ev->button();
  
  if (m_mouseButton == LeftButton)
  {
    m_moving = None;

    if (m_mode == DSO)
    {
      if (Off != m_timeMarkerMode && abs(m_x-m_timeMarker[0]) < 3)
      {
        m_moving = Time1;
      }
      else if (RatioMarker == m_timeMarkerMode && abs(m_x-m_timeMarker[1]) < 3)
      {
        m_moving = Time2;
      }
      else if (Off != m_timeMarkerMode && abs(m_x-m_timeMarker[2]) < 3)
      {
        m_moving = Time3;
      }
      else if (m_showAmplitudeMarker && abs(m_y-m_amplitudeMarker[0]) < 3)
      {
        m_moving = Amplitude1;
      }
      else if (m_showAmplitudeMarker && abs(m_y-m_amplitudeMarker[1]) < 3)
      {
        m_moving = Amplitude2;
      }
    }
    else if (m_mode == FFT)
    {
      if (Off != m_timeMarkerMode && abs(m_x-m_freqMarker) < 3)
      {
        m_moving = Freq;
      }
      else if (m_showAmplitudeMarker && abs(m_y-m_dbMarker[0]) < 3)
      {
        m_moving = Db1;
      }
      else if (m_showAmplitudeMarker && abs(m_y-m_dbMarker[1]) < 3)
      {
        m_moving = Db2;
      }
    }
  }
}

void
DsoWid::mouseReleaseEvent( QMouseEvent */*ev*/ )
{
  m_mouseButton = NoButton;
}

void
DsoWid::fft( int channel )
{
  // apply taper window
  //
  if (m_fftWindow)
  {
    for (int i=0; i> 1);
  
  if (Power == m_fftType)
  {
    for (int i=0; i> 1); ++i)
      {
        m_fftOutSum[channel][i] -= m_fftOutBuffer[channel][m_fftOutAvPointer[channel]][i];
      }
    }
    
    // store current data at current pointer position
    //
    memcpy( m_fftOutBuffer[channel][m_fftOutAvPointer[channel]], m_fftOut, (fftSize() >> 1)*sizeof(float) );
    
    // increase pointer (modulo length)
    //
    m_fftOutAvPointer[channel] = (m_fftOutAvPointer[channel] + 1) % m_fftAverageBufferLength;
    
    // increase counter (and clamp to max length) while filling average buffer
    //
    m_fftOutAvCounter[channel]++;
    
    if (m_fftOutAvCounter[channel] > m_fftAverageBufferLength) 
    {
      m_fftOutAvCounter[channel] = m_fftAverageBufferLength;
    }
    
    // now add new values and divide by length of buffer
    //
    for (int i=0; i> 1)*sizeof(float) );
  } 
  else if (Minimum == m_fftDisplayMode)
  {
    for (int i=0; i> 1)*sizeof(float) );
  } 
  
  // now find max peak, so we can show dbc
  //
  m_maxFftVal = 0.0f;
  for (int i=1; i= 1000.)
      {
        value /= 1000.;
        unit = "kHz";
      }
      if (value >= 1000.)
      {
        value /= 1000.;
        unit = "MHz";
      }
      if (value >= 1000.)
      {
        value /= 1000.;
        unit = "GHz";
      }
      
      retString.setNum( value, 'f', 8 );
    }
    break;
    
  case Time:
    {
      unit = "s";
      if (value < 1.0)
      {
        value *= 1000.;
        unit = "ms";
      }
      if (value < 1.0)
      {
        value *= 1000.;
        unit = "\265s";
      }
      if (value < 1.0)
      {
        value *= 1000.;
        unit = "ns";
      }
      
      retString.setNum( value, 'f', 8 );
    }
    break;
    
  case Voltage:
    {
      unit = "V";     
      if (value < 0.5)
      {
        value *= 1000.;
        unit = "mV";
      }
      if (value < 0.5)
      {
        value *= 1000.;
        unit = "V";
      }
      
      retString.setNum( value, 'f', 8 );
    }
    break;
    
  case Ratio:
    {
      if (value == 0)
      {
        retString = "";
      }
      else
      {
        if (value < 1)
        {
          if (1./value < 100)
          {
            QString str;
            str.setNum( 1./value, 'f', prec );
            retString = "1:";
            retString += str;
          }
          else
          {
            retString.sprintf( "1:%d", (int)(1./value) );
          }
        }
        else
        {
          if (value < 100)
          {
            retString.setNum( value, 'f', prec );
            retString += ":1";
          }
          else
          {
            retString.sprintf( "%d:1", (int)value );
          }
        }
      }
    }
    break;
  }
  
  if (type != Ratio)
  {
    int cnt=0;
    bool separator=false;
    
    for (unsigned i=0; i= prec && separator)
      {
        retString = retString.left(i);
        if (retString[i-1] == '.')
        {
          retString = retString.left(i-1);
        }
        break;
      }
      
      if (retString[i].isNumber()) cnt++;
      if (retString[i] == '.') separator = true;
    }
  }
  
  retString += unit;
  
  return retString;    
}

float
DsoWid::rms( int channel )
{
  float rms = 0.0;
  
  const int acqPlus = m_dso->acqOffset() + m_dso->acqLength();
  
  for (int i=m_dso->acqOffset(); iacqLength();  
  rms = sqrt( rms );    
  rms = volts( channel, rms ) * m_probe[channel];
  
  return rms;
}

void
DsoWid::setFftBufferLength( int len )
{
  flushFftAverageBufferSLOT();
  
  for (int i=0; irunning()) { usleep(20000); }
  }
  
  delete m_dso;
  m_dso = 0;
  delete m_simWid;
  m_simWid = 0;
  
  Simulator *sim;
  
  switch (model)
  {
  case Dso::Simulator:
    m_simWid = new SimulatorWid;
    m_simWid->show();
    sim = new Simulator;
    m_dso = sim;
    connect( m_simWid, SIGNAL( waveformChanged( unsigned, Simulator::Waveform )),
             sim, SLOT( waveformChangedSLOT( unsigned, Simulator::Waveform )));
    connect( m_simWid, SIGNAL( frequencyChanged( unsigned, float )),
             sim, SLOT( frequencyChangedSLOT( unsigned, float )));
    connect( m_simWid, SIGNAL( vppChanged( unsigned, float )),
             sim, SLOT( vppChangedSLOT( unsigned, float )));
    connect( m_simWid, SIGNAL( noiseToggled( unsigned, bool )),
             sim, SLOT( noiseToggledSLOT( unsigned, bool )));
    connect( m_simWid, SIGNAL( jitterToggled( unsigned, bool )),
             sim, SLOT( jitterToggledSLOT( unsigned, bool )));
    connect( m_simWid, SIGNAL( phaseJitterToggled( unsigned, bool )),
             sim, SLOT( phaseJitterToggledSLOT( unsigned, bool )));
    connect( m_simWid, SIGNAL( symetryChanged( unsigned, int )),
             sim, SLOT( symetryChangedSLOT( unsigned, int )));
    
    init();
    m_dso->start();
    break;
    
  case Dso::PCS64i:
    m_dso = new Pcs64i;
    init();
    m_dso->start();
    break;
    
  default:
    std::cerr << "NOT IMPLEMENTED" << std::endl;
    return false;
    break;
  }
  
  m_model = model;
  
  if (m_dso)
  {
    m_dataStretch = 255.0 / (float)m_dso->maxValue();
  }
  
  emit dsoChanged();
  
  return true;
}

void
DsoWid::init()
{
  delete [] m_data[0];
  delete [] m_data[1];
  delete [] m_data[2];
  
  delete m_arr;
  delete m_arr2;
  delete m_arr3;
  
  m_data[0] = new int [m_dso->numSamples()];
  m_data[1] = new int [m_dso->numSamples()];
  m_data[2] = new int [m_dso->numSamples()];  // this buffer is used for math
  
  m_arr = new QPointArray( 5*WINDOW_X_PIXELS );
  m_arr2 = new QPointArray( m_dso->numSamples() );
  m_arr3 = new QPointArray( FFT_SIZE_MAX );
}

void
DsoWid::internalAdjustSize()
{
  m_fh = fontMetrics().height();
  m_tw = fontMetrics().width( "999.9MHz" );
    
  m_legWidth = fontMetrics().width( "100%" ) + 14;
  
  m_x0 = 1+5 + m_legWidth;
  m_y0 = 1+2 + m_fh + 5;
  
  setFixedSize( WINDOW_X_PIXELS+1+8+5+m_x0, WINDOW_Y_PIXELS+2*m_fh+8+10 );
}

void
DsoWid::applyPrefsSLOT()
{
  internalAdjustSize();
  
  //const int off = s_lineWidth / 2;
  //double aStep = (1.5708-0.201)/(double)off;
  //double a = 0.201;
}

void
DsoWid::setChannelOpSLOT( bool invCh1, bool absCh1, bool invCh2, bool absCh2 )
{
  m_invCh[0] = invCh1;
  m_absCh[0] = absCh1;
  m_invCh[1] = invCh2;
  m_absCh[1] = absCh2;
  
  applyPrefsSLOT();
  
  update();
}

void
DsoWid::fastAcqSLOT( bool on )
{
  m_dso->setFastAcq( on );
  setDsoAcqLength();
}

void
DsoWid::equivalentSamplingSLOT( bool on )
{
  m_dso->setEquivalentSampling( on );
}

void
DsoWid::histogramSLOT()
{
  if (!m_converterHistogram)
  {
    m_converterHistogram = new ConverterHistogramDlg( this );
  }
  
  m_converterHistogram->raise();
  m_converterHistogram->show();
}

void
DsoWid::dcOffsetSLOT()
{
  setDcOffsetSLOT( 0, m_averageSample[0] );
  setDcOffsetSLOT( 1, m_averageSample[1] );
}