📄 waveformdisplay.cpp
字号:
// Revert to the original pen.
//
pDC->SelectObject(pcPenOld);
}
//*****************************************************************************
//
// Calculates the horizontal pixel offset required to center the waveform
// trace on the display.
//
// \param pCapData points to a structure containing information on the
// oscilloscope data which is to be rendered.
//
// This function is called to calculate the correct offset required to
// position the rendered waveform such that the middle sample in the
// waveform will appear at the center of the display area.
//
// \return The required offset to correctly center the waveform.
//
//*****************************************************************************
long CWaveformDisplay::GetCenterOffset(void)
{
long lX;
long lCenterIndex;
//
// If called before we have anything to render, just return 0.
//
if(m_pScopeData == NULL)
{
return(0);
}
//
// First determine where the center sample is. If this is dual channel
// data, the number of sample periods is effectively half what it would
// be if we were dealing with single channel data.
//
lCenterIndex = m_pScopeData->ulTotalElements / 2;
//
// Now determine the total number of microseconds from the start of the
// buffer to the center sample.
//
lX = lCenterIndex * m_pScopeData->ulSamplePerioduS;
//
// Convert from microseconds to pixels given the selected timebase. At
// this point, we have a distance in pixels from the earliest sample.
//
lX = (lX * m_iGraticuleSide)/m_uluSPerDivision;
//
// The correction we apply to the x coordinates is the center offset minus
// half the width of the display.
//
return(lX - (m_sizeWaveform.cx / 2));
}
//***************************************************************************
//
// Update the timebase and, optionally, redraw the waveform.
//
//***************************************************************************
void CWaveformDisplay::SetTimebase(unsigned long uluSPerDivision, BOOL bUpdate)
{
m_uluSPerDivision = uluSPerDivision;
if(bUpdate)
{
InternalRenderWaveform();
}
}
//***************************************************************************
//
// Update the trigger level and, optionally, redraw the waveform.
//
//***************************************************************************
void CWaveformDisplay::SetTriggerLevel(long lTriggerLevelmV, BOOL bUpdate)
{
m_lTriggerLevelmV = lTriggerLevelmV;
if(bUpdate)
{
InternalRenderWaveform();
}
}
//***************************************************************************
//
// Update the trigger position and, optionally, redraw the waveform.
//
//***************************************************************************
void CWaveformDisplay::SetTriggerPos(int iTriggerPos, BOOL bUpdate)
{
//
// The supplied trigger position is in the range (-60, 60) to match
// the waveform display size on the device. Rescale this so that
// the same bounds map to the complete width of the window here.
//
m_iTriggerPos = (iTriggerPos * m_sizeWaveform.cx) /
(TRIGGER_POS_SLIDER_MAX - TRIGGER_POS_SLIDER_MIN);
if(bUpdate)
{
InternalRenderWaveform();
}
}
//***************************************************************************
//
// Update the vertical offset for one channel and, optionally, redraw the
// waveform.
//
//***************************************************************************
void CWaveformDisplay::SetChannelPos(int iChannel, long lVerticalOffsetmV,
BOOL bUpdate)
{
ASSERT((iChannel == CHANNEL_1) || (iChannel == CHANNEL_2));
m_lVerticalOffsetmV[iChannel] = lVerticalOffsetmV;
if(bUpdate)
{
InternalRenderWaveform();
}
}
//***************************************************************************
//
// Update the vertical scale for one channel and, optionally, redraw the
// waveform.
//
//***************************************************************************
void CWaveformDisplay::SetChannelScale(int iChannel,
unsigned long ulmVPerDivision,
BOOL bUpdate)
{
ASSERT((iChannel == CHANNEL_1) || (iChannel == CHANNEL_2));
m_ulmVPerDivision[iChannel] = ulmVPerDivision;
if(bUpdate)
{
InternalRenderWaveform();
}
}
//*****************************************************************************
//
// Convert a sample index in the current data set into a horizontal (x)
// pixel coordinate on the display.
//
//*****************************************************************************
long CWaveformDisplay::SampleIndexToX(unsigned long ulSampleIndex, int iChannel)
{
long lX;
bool bApplyOffset;
//
// If we don't have any dataset to play with, just return 0.
//
if(m_pScopeData == NULL)
{
return(0);
}
//
// Determine whether or not we need to apply the second sample time
// offset.
//
bApplyOffset = (((iChannel == CHANNEL_1) &&
(m_pScopeData->bCh2SampleFirst)) ||
((iChannel == CHANNEL_2) &&
(!m_pScopeData->bCh2SampleFirst)));
//
// First determine the number of microseconds from the start of the capture
// to this sample. Note that we ensure we add the sample offset if this is
// a sample for the second channel.
//
// ulSampleIndex <= 1024 (typically 512)
// ulSamplePerioduS <= 10000 (min rate of 10mS per sample or 100Hz)
// ulSampleOffsetuS <= 8
//
// so lX <= 10240008, hence no overflow in this calculation.
//
lX = (ulSampleIndex * m_pScopeData->ulSamplePerioduS) +
(bApplyOffset ? m_pScopeData->ulSampleOffsetuS : 0);
//
// Now convert from microseconds to pixels given the selected timebase. At
// this point, we have a distance in pixels from the earliest sample.
//
lX = (lX * m_iGraticuleSide)/m_uluSPerDivision;
//
// Adjust the pixel position for the current display window. We center the
// window on the full trace then apply an offset to allow the user to pan
// the window across the captured waveform (effectively changing the
// trigger position from their point of view).
//
lX = lX - GetCenterOffset() + m_iTriggerPos;
//
// All done. Return the result to the caller.
//
return(lX);
}
//*****************************************************************************
//
// Convert an ADC sample to a Y coordinate on the display taking into account
// all display parameters in the passed rendering structure.
//
//*****************************************************************************
long CWaveformDisplay::SampleToY(short sSamplemV, int iChannel)
{
long lY;
long lMillivolts;
//
// Adjust this with the client-supplied offset value for the channel.
//
lMillivolts = sSamplemV + m_lVerticalOffsetmV[iChannel];
//
// Calculate the actual y coordinate representing this voltage given the
// voltage scaling supplied by the client.
//
lY = MV_TO_Y(lMillivolts, m_ulmVPerDivision[iChannel]);
return(lY);
}
//*****************************************************************************
//
// Draw the waveform for a single set of captured samples.
//
//*****************************************************************************
void CWaveformDisplay::DrawSingleWaveform(CDC *pDC, int iChannel, COLORREF clrColor)
{
unsigned long ulLoop;
short sSample;
long lX;
long lY;
CPen cPen(PS_SOLID, 1, clrColor);
CPen cDotPen(PS_DOT, 1, clrColor);
CPen *pOldPen;
//
// Check for cases where we are asked to render a waveform for a channel
// whose data we don't have.
//
if(!m_pScopeData->bDualChannel)
{
//
// This is single channel data. Does it contain the channel we are
// being asked to draw?
//
if(((iChannel == CHANNEL_2) && !m_pScopeData->bCh2SampleFirst) ||
((iChannel == CHANNEL_1) && m_pScopeData->bCh2SampleFirst))
{
return;
}
}
//
// Draw the ground level if required.
//
if(m_bShowGround)
{
//
// Select the dotted pen.
//
pOldPen = pDC->SelectObject(&cDotPen);
//
// Determine where the ground line should go.
//
lY = SampleToY(0, iChannel);
//
// If ground is visible, draw the line.
//
if((lY >= 0) && (lY < m_sizeWaveform.cy))
{
pDC->MoveTo(0, lY);
pDC->LineTo(m_sizeWaveform.cx, lY);
}
pDC->SelectObject(pOldPen);
}
//
// Set the solid pen for the waveform.
//
pOldPen = pDC->SelectObject(&cPen);
//
// Loop through each of the captured samples to draw the waveform onto
// the display.
//
for(ulLoop = 0; ulLoop < m_pScopeData->ulTotalElements; ulLoop++)
{
//
// Get the screen X coordinate for this sample number given the
// renderer timebase and offset.
//
lX = SampleIndexToX(ulLoop, iChannel);
//
// Get the screen Y coordinate for this sample given the renderer
// voltage scaling factor and offset.
//
sSample = GetSample(ulLoop, iChannel);
lY = SampleToY(sSample, iChannel);
//
// If this is not the first pixel, draw a line segment
//
if(ulLoop)
{
//
// Draw one line segment.
//
pDC->LineTo(lX, lY);
}
else
{
//
// Move the cursor to the start position.
//
pDC->MoveTo(lX, lY);
}
}
//
// Reinstate the old pen.
//
pDC->SelectObject(pOldPen);
}
//***************************************************************************
//
// Return the ulIndex-th sample for channel iChannel from the current
// dataset taking into account all the weird and wonderful combinations of
// single vs dual channel capture and channel swapping.
//
//***************************************************************************
short CWaveformDisplay::GetSample(unsigned long ulIndex, int iChannel,
unsigned long *pulTime)
{
ASSERT(m_pScopeData);
//
// Do we have single or dual channel data?
//
if(m_pScopeData->bDualChannel)
{
//
// Dual channel data.
//
if(((iChannel == CHANNEL_1) && !m_pScopeData->bCh2SampleFirst) ||
((iChannel == CHANNEL_2) && m_pScopeData->bCh2SampleFirst))
{
if(pulTime)
{
*pulTime = ulIndex * m_pScopeData->ulSamplePerioduS;
}
return(m_pDualElement[ulIndex].sSample1mVolts);
}
else
{
if(pulTime)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -