利用VC绘制对数坐标系
目标:绘制以任意频率为起点、任意频率为终点的对数坐标,并能对坐标进行任意缩放。
实现方法:
======【Update 2013-12-18】:=========================================================
- 今天发现以前的方法其实是很笨的方法。其实只要知道任意两个点的X坐标及其对应的频率f, 就可以根据第三点的频率确定第三点的坐标,或者根据第三点的坐标确定该点的频率。个人感觉对数坐标的本质就是,对于频率取对数之后,该坐标映射成为线性坐标,即屏幕坐标x可以看做log(f)的线性函数。
-
- 给定三点(x0,f0), (x1,f1),(x2,f2), 其中x是X坐标,f是对应的频率,有对应关系
- (x2-x0)/(x1-x0) = (log(f2)-log(f0))/(log(f1)-log(f0)) = log(f2/f0)/log(f1/f0),
- 于是,可以求得
- x1=log(f1/f0)/log(f2/f0)*(x2-x0)+x0,
- 和
- log(f1) = 1/C* log(f2/f0)+log(f0), where C=(x2-x0)/(X1-x0)。
-
- 根据这两个公式,写成两个函数,一个是根据频率计算x坐标,一个根据x坐标计算频率,然后指定坐标轴的位置,就可以很容易绘制出对数坐标系了。
以下内容已经过时,仅供参考。
1、定义
标准频率:频率值为10N的相应的频率点,其中N=…-2,-1,0,1,2,3…,单位MHz。如0.01MHZ、0.1MHz、1MHz、10MHz、100MHz等都是标准频率。
2、思路
首先找出标准频率,画出标准频率线,然后根据标准频率画出其他频率线。
3、实现
(1)对数坐标系数学基础
如图所示的对数坐标系中:
由此,若知道上图中的三个点的坐标,可以求出另一点的坐标。
(2)找出起始|终止标准频率的代码:
double stdStartFreq,stdStopFreq;// 标准频率起始点、终止点
// 格式化标准起始频率
for(int i=0;i<7;++i)
{
if(StartFreq==pow((double)10,i-2))
{
stdStartFreq=StartFreq;
break;
}
else if(StartFreq>pow((double)10,i-2) && StartFreq<pow((double)10,i-1))
{
stdStartFreq=pow((double)10,i-1);
break;
}
}
// 格式化标准终止频率
for(int i=0;i<7;++i)
{
if(StopFreq==pow((double)10,i-2))
{
stdStopFreq=StopFreq;
break;
}
else if(StopFreq>pow((double)10,i-2) && StopFreq<pow((double)10,i-1))
{
stdStopFreq=pow((double)10,i-2);
break;
}
}
起始标准频率就是第一个大于起始频率的标准频率;终止标准频率就是最后一个小于终止频率的标准频率。
举例:如果起始频率为1.9MHz,终止频率为201MHz,那么起始标准频率为10MHz,终止标准频率为100MHz;如果起始频率为1.9MHz,终止频率为20.1MHz,那么起始标准频率为10MHz,终止标准频率为10MHz;如果起始频率为1.9MHz,终止频率为2.01MHz,那么起始标准频率为10MHz,终止标准频率为1MHz。
(3)三种情况
①起始频率≤起始标准频率<终止标准频率≤终止频率
在这种情况下,首先画出起始|终止标准频率线。对于起始频率和起始标准频率之间的部分,由起始标准频率递减向起始频率画出虚线;对于终止标准频率和终止频率之间的部分,由终止标准频率向终止频率递增画出虚线;对于起始标准频率和终止标准频率之间的部分,首先找到二者之间的标准频率,然后根据标准频率(包括起始|终止标准频率)画出其间的虚线。
②起始频率<起始标准频率=终止标准频率<终止频率
这种情况是第一种情况的特例。因为起始标准频率=终止标准频率,所以第一种情况里只有两种情况来画虚线。
③终止标准频率<起始频率<终止频率<起始标准频率
在这种情况下,先找出终止标准频率,然后依据终止标准频率递增画出起始频率和终止频率之间的虚线。
4、其他问题
(1)细化间隔
由于对数坐标相同频率段的间隔不同,如10~20MHz的间隔与80~90MHz的间隔不同,前者大于后者;另外,考虑到坐标系放大时,如200MHz~300MHz频率段,整个坐标系中无虚线,只有横线,很难对测试曲线中某个点的频率进行粗略判断。因此,对于比较大的间隔,应该进行细化。
方法就是利用相邻两虚线的间隔所占坐标横轴的比例进行判断,若大于某个数值则有必要进行细化。由于间隔有大有小,考虑到细化的间隔不能影响整体对数坐标的效果,因此细化的竖线用灰色虚线表示,且将间隔分成不同等级,不同等级画不同条灰色虚线。
(2)坐标标注
坐标标注同样利用相邻两虚线的间隔所占坐标横轴的比例来进行判断,大于某个设定值后才进行标注,否则不进行标注。
【源代码如下:】
void CEMCView::DrawLogCoords(CDC * pDC, double StartFreq, double StopFreq)
... {
CString note;
CRect rect;
this->GetClientRect(&rect);
ORIGIN_X=rect.left+rect.Width()/10; // 坐标原点X坐标
ORIGIN_Y=rect.bottom-rect.Height()/10; // 坐标原点Y坐标
COORD_TOP=rect.top+rect.Height()/10; // 坐标系最高点Y坐标
COORD_RIGHT=rect.right-rect.Width()/10; // 坐标系最右点X坐标
pDC->MoveTo(ORIGIN_X,ORIGIN_Y);
pDC->LineTo(ORIGIN_X,COORD_TOP); // 画纵坐标轴
pDC->MoveTo(ORIGIN_X,ORIGIN_Y);
pDC->LineTo(COORD_RIGHT,ORIGIN_Y); // 画横坐标轴
note.Format(L"%4.2fM",StartFreq);
pDC->TextOutW(ORIGIN_X-15,ORIGIN_Y+5,note); // 原点标注
// 画横格线
double yInc=(ORIGIN_Y-COORD_TOP)/9.0;
double tmp=(double)ORIGIN_Y-yInc;
for(int i=0;i<8;++i)
...{
pDC->MoveTo(ORIGIN_X,(int)tmp);
pDC->LineTo(COORD_RIGHT,(int)tmp);
note.Format(L"%d",10*(i+1));
pDC->TextOutW(ORIGIN_X-25,(int)tmp-8,note); // z纵坐标标注
tmp-=yInc;
}
pDC->MoveTo(ORIGIN_X,COORD_TOP);
pDC->LineTo(COORD_RIGHT,COORD_TOP);
note.Format(L"%d",90);
pDC->TextOutW(ORIGIN_X-25,COORD_TOP-8,note);// z纵坐标标注
note.Format(L"[dBuV]");
pDC->TextOutW(ORIGIN_X-25,COORD_TOP-28,note);//