第一部分:实时频谱分析自定义绘制层

构造参数决定行为:
| 枚举 | 含义 |
|---|---|
ENRTSABaseType(m_basetype) |
图形类型:普通频谱 / 放大 / PvT / 概率密度(Thick) 等 |
ENRTSAColorType(m_colortype) |
是否显示右侧色温条:Color_Y 有,Color_N 无 |
TraceView 与 paint 的分工:
| 模式 | TraceView | paint |
|---|---|---|
Base_Spec / Base_SpecLine / Base_PvT 等 |
白色频谱迹线 + 网格 | 参数文字、色条(若开启)、Marker |
Base_Thick(频率功率/概率密度,默认 DisplayType=4) |
仅网格/坐标轴 | 密度图 + 白色迹线 + 色条 + 标注 + Marker |
Base_Thick 模式下 不调用 TraceView::UpdateTraceData,迹线完全由 paint → DrawTraceData 绘制。
1.2 paint 主流程(七步)
第一步:初始化绘制环境
painter->save()保存画布状态。- 设置主题文字色、12pt 字体、文字抗锯齿。
- 通过
pixMapper->GraphRect计算绑图区及顶部/底部文字 Y 坐标。
第二步:语言分支
Language == 0:英文标注;否则中文。结构相同,仅字符串不同。
第三步:按 BaseType 绘制参数文字(仅文字,无图形数据)
| BaseType | 顶部 | 底部 |
|---|---|---|
Base_Spec / SpecLine / SpecLine2 |
Y 轴刻度、参考电平 | 中心频率、扫宽、RBW、采集时间 |
Base_Magnify / Base_Line |
同上 | Zone 中心频率、Zone 扫宽 |
Base_PvT |
--- | RBW、刻度、采集时间 |
Base_Thick |
Y 轴刻度、参考电平 | 中心频率、扫宽、RBW、采集时间 |
数值由 SAUnits 自动格式化(dB、dBm、Hz、s)。
第四步:绘制右侧色温条(详见第二部分)
触发 :m_colortype == Color_Y 或 m_basetype == Base_Thick。
第五步:概率密度 + 白色迹线(仅 Base_Thick)
- 色条旁写五档百分比标签:100.0%、14.9%、2.2%、0.3%、0.0%。
DrawPointData:底层彩色密度图。DrawTraceData:顶层白色瞬时谱线。
绘制顺序:密度(底)→ 迹线(顶)。
第六步:Marker 信息(所有 BaseType)
右上角显示当前选中标记的频率/幅度或 Delta 差值;频率精度随 Span/SweepPoint 自适应。
第七步:painter->restore() 结束。
1.3 DrawPointData --- 概率密度图
概率密度图是统计信号在「频率 + 幅度」二维平面上出现的频次 / 概率,专门抓瞬态、跳变、间歇性、低占空比信号,是普通频谱(迹线)做不到的。
数据 :m_BitMapData,256 行(功率档)× N 列(频率档),m_Scale/m_Offset 将行索引映射为 dBm。
步骤:
- 空数据则返回;扫描矩阵求
maxColor归一化。 - 每行功率 → 像素 Y(
GetYMaxData/GetYMinData)。 - 按屏幕宽度合并水平像素,避免过密。
- 非零单元:
命中比 = 值/maxColor→GetSelectColor(colors, 命中比)→fillRect。
密度图与右侧色条共用同一 colors 向量,保证图例与图像一致。
1.4 DrawTraceData --- 白色瞬时迹线
数据 :m_TraceData,长度 m_TraceNum。
- 点数 ≥ 像素宽:
GetCompressedData每列取 max/min,画竖线段(保峰)。 - 点数 < 像素宽:
GetEnlargedData扩展宽度,画折线。 - Y 坐标经
pixMapper->YValueToPixel转换;画笔白色,drawLines批量绘制。
1.5 多类型策略总览
第三步:参数文字(所有类型)
↓
Color_Y 或 Base_Thick ? → 第四步:色温条
↓
Base_Thick ? → 第五步:密度 + 白线
↓
第六步:Marker(所有类型)
1.6 数据流
Base_ReceiveData
├─ 所有模式:Trace1 → m_TraceData
├─ Base_Thick:BitMap → m_BitMapData,设置 Scale/Offset/BitMapNum,不调 UpdateTraceData
└─ 其他模式:UpdateTraceData + SignalUpdate(迹线由 TraceView 画)
paint 只读 rtasParam、markerParam,不修改数据。
第二部分:色温系统专题
色温逻辑是 AllColorData() 预生成,paint 运行时查表使用。
2.1 全局状态变量
| 静态变量 | 默认值 | 作用 |
|---|---|---|
m_AllColor |
--- | 6 套完整调色板(QMap,key=0~5) |
m_ColorType |
1 | 当前使用的方案(对应 ParamCode::ColorSelect) |
m_MaxColor |
100 | 色条顶部参考位置 %(FeferColorLocat) |
m_MinColor |
0 | 色条底部位置 %(BottomColorLocat) |
m_MaxYData / m_MinYData |
0 / -100 | 密度图 Y 轴 dBm 范围 |
用户参数映射(RtsaMainWidget::OnParamChanged):
ColorSelect→SetColorType(index)→ 切换 6 套配色FeferColorLocat→SetFeferData→ 色条顶部裁剪BottomColorLocat→SetBottomData→ 色条底部裁剪ReferenceLevel/AxisYScale→SetYRange→ 密度图 Y 映射
2.2 AllColorData --- 六套调色板预生成
调用时机 :RtsaMainWidget 构造时 RtsaPublicFunc::AllColorData(),只执行一次。
基本参数 :colorBarLength = 1530(约 255×6 级渐变),tempLength = 255 为每段长度。
方案 0 --- 冷色
按顺序生成 4 段 RGB 线性渐变:
| 段 | 渐变方向 | 视觉 |
|---|---|---|
| 1 | B↑(蓝→青) | (0, t, 1) |
| 2 | G↓(青→绿) | (0, 1, 1-t),步长 2 |
| 3 | R↑(绿→黄) | (t, 1, 0),步长 2 |
| 4 | G↓(黄→红) | (1, 1-t, 0) |
方案 1 --- 暖色
6 段完整彩虹环,每段 255 色:
| 段 | 渐变 |
|---|---|
| 1 | 红→黄 (1, t, 0) |
| 2 | 黄→绿 ((2L-i)/L, 1, 0) |
| 3 | 绿→青 (0, 1, t) |
| 4 | 青→蓝 (0, 1-t, 1) |
| 5 | 蓝→品 (t, 0, 1) |
| 6 | 品→红 (1, 0, 1-t) |
共约 1530 色。后续 AbstractColor 对暖色只取 前 4/6(约 1020 色)作为有效范围,对应红→蓝主视觉,去掉尾部冗余段。
方案 2 --- 灰色
灰度从 (153/255) 线性降至 (30/255),约 123 级。
方案 3 --- 雷达色
两段:红→黄(255 色)+ 黄→绿(255 色),共 510 色。
方案 4 --- 火热色
单段红→黄 (1, (255-i)/255, 0),255 色。
方案 5 --- 冰霜色
两段:浅蓝白 (i,i,1) 从 255→170;再过渡到 (i,i,(85+i)/255) 至 0。
设计意图:启动时一次性生成高分辨率色表,运行时零计算开销,仅做索引抽样。
2.3 AbstractColor --- 按绑图高度适配色表
入口 :AbstractColor(unsigned int width),width = 绑图区像素高度。
第一步 :按 m_ColorType 取 veccolor = m_AllColor[m_ColorType],并确定有效色数 pointnum(暖色取 count/6*4,其余取全部)。
第二步 :按 width 与 pointnum 关系分支:
| 情况 | 策略 |
|---|---|
width == 0 |
直接返回完整 veccolor |
width < pointnum(色多屏少) |
抽取:均匀跳采样,每屏像素对应色表中一个颜色 |
width > pointnum(色少屏多) |
重复 :每个原色重复 absnum 或 absnum+1 次填满高度 |
抽取/重复使用「商 + 余数分配」算法:前 absheight 行多分配 1 个色阶,保证总输出长度恰好等于 width。
输出 :长度 = width 的 QVector<QColor>,每像素一行对应一个颜色。
2.4 GetColorLocat --- 色条最终输出
调用链 :GetColorLocat(width) → AbstractColor(width) → 可选二次裁剪。
GetColorLocat(allnum)
│
├─ colors = AbstractColor(allnum) // 已适配高度 + 当前 ColorType
│
├─ 若 m_MaxColor==100 且 m_MinColor==0
│ → 直接返回 colors(全范围色条)
│
└─ 否则二次裁剪:
Allnum = (m_MaxColor - m_MinColor) / 100 × colors.count()
从 colors 中再均匀抽取 Allnum 个颜色
→ 返回 tempcolor(缩短后的色条)
含义:
m_MaxColor = 100、m_MinColor = 0:色条覆盖全高度(默认)。- 用户调
FeferColorLocat(如 80%):m_MaxColor=80,色条只保留原色表的上 80% 段,低密度区颜色被裁掉,视觉上「热色区」压缩。 BottomColorLocat控制底部裁剪,两者共同定义有效色温显示区间。
GetFeferData() 返回 m_MaxColor/100,供 paint 做垂直偏移。
2.5 paint 中绘制右侧色温条
位置 :RtsaSpecBaseItem::paint 第四步。
第一步:前置检查
- 仅当
Color_Y或Base_Thick时进入。 - 若
GraphRect.bottom < GraphRect.top,直接return(区域无效)。
第二步:计算几何参数
| 参数 | 值 | 含义 |
|---|---|---|
allnum |
bottom - top |
色条高度 = 绑图区像素高 |
colorwidth |
20 | 色条宽度(px) |
rightmargin |
2 | 与绑图区右边缘间距 |
Xposition |
GraphRect.right + 2 |
色条左 X |
startnum |
top + (1-GetFeferData())×allnum |
垂直起始 Y(Fefer 偏移) |
第三步:取色
colors = GetColorLocat(allnum),长度 =allnum。
第四步:逐行 fillRect
- 循环
index = 0 ... colors.count()-1:QRect(Xposition, startnum + index, 20, 1)painter->fillRect(rect, colors[index])
- 每行 1 像素高,20 像素宽,拼成竖直渐变色条。
第五步(Base_Thick 专属):百分比标签
- 调用
DrawTextYAxisColor(painter, "100.0%", mode, width),mode0~4 分别对应顶、1/4、1/2、3/4、底。 - 文字 X =
GraphRect.right + colorwidth + rightmargin,与色条对齐。 - 五档标签与
GetSelectColor的四段阈值一致(100%、14.9%、2.2%、0.3%、0.0%)。
第六步:密度图复用同一 colors
DrawPointData(painter, colors)传入相同向量,命中比查同一色表,保证色条与密度图颜色语义一致。
2.6 GetSelectColor --- 命中比到颜色的非线性映射
密度图每个像素的 percennum = 矩阵值 / maxColor(0~1),映射规则:
| percennum 范围 | 色条区间 | 对应标签 |
|---|---|---|
| ≥ 14.9% (0.149) | 第 1 段(最热) | 100.0% |
| 2.2% ~ 14.9% | 第 2 段 | 14.9% |
| 0.3% ~ 2.2% | 第 3 段 | 2.2% |
| < 0.3% | 第 4 段(最冷) | 0.3% / 0.0% |
色条被均分为 4 段(everynum = count/4),段内线性插值索引。低频命中→冷色,高频命中→暖色。
2.7 色温系统数据流总图
启动: AllColorData() → m_AllColor[0~5]
运行时参数:
ColorSelect → m_ColorType
FeferColorLocat → m_MaxColor
BottomColorLocat → m_MinColor
每帧 paint:
allnum = 绑图高度
colors = GetColorLocat(allnum)
└─ AbstractColor(allnum)
└─ m_AllColor[m_ColorType] 抽样/重复
└─ 按 Max/Min 二次裁剪
fillRect × allnum → 右侧色条
GetSelectColor(colors, 命中比) → 密度图 fillRect
第三部分:VSA 数字解调 --- 星座图与眼图
星座图与眼图是 VSA 数字解调两大核心分析视图,二者互补定位信号质量。星座图基于 IQ 复平面,每个点位对应一个传输符号,主要评估幅相精度。点位集中规整代表调制质量优异;点位弥散、偏移、出现杂点,说明存在噪声、IQ 失衡、相位偏差、功放非线性等问题,配套 EVM、MER 等指标可量化调制误差,常用于发射机、射频链路故障排查。
眼图由基带符号波形按时钟叠加而成,侧重分析时域信号完整性。眼孔开阔、线条纤细,代表码间干扰(ISI)、噪声、抖动小;眼孔收窄、边缘粗糙、水平扩散,意味着带宽不足、时序抖动大、同步异常。通过眼高、眼宽可判断噪声与时序裕量。
实际测试中,星座图查调制与射频失真,眼图看基带与时序问题,结合两项视图能快速定位数字通信链路的各类缺陷。
三层图像:
| 图层 | 内容 |
|---|---|
_backgroundLayer |
黑底 + 网格 + 刻度 |
_foregroundLayer |
星座/眼图曲线 |
_clickedLayer |
鼠标点击读数 |
3.2 数据传入格式与顺序
测量端
I_constellation[i]、Q_constellation[i]:解调后第 i 个符号的 I/Q。ConstellationLen:有效点数 N。
| 键 | 含义 |
|---|---|
kIData |
I 数组指针 |
kQData |
Q 数组指针 |
kIQLength |
N |
kSymRate |
符号率 |
ModFormat / SymbolRate |
测量参数,影响解调,不直接传入 Draw |
合成 Complex
索引: 0 1 2 ... N-1
I: I[0] I[1] I[2] ... I[N-1]
Q: Q[0] Q[1] Q[2] ... Q[N-1]
→ Complex(I[i], Q[i]),按符号时间顺序排列
上限 MaxIQLen = 2002;Render 内再截断至 16384。
Perform 归一化(每帧)
求 I/Q 各自 min/max,映射到约 [-0.9, 0.9],避免帧间幅度差导致显示缩放跳动。
3.3 模式配置与颜色
InitChartWidget 当前配置:
| Widget | SetModel | 设计意图 |
|---|---|---|
m_pDiagramWidget |
kConsole, true |
星座图 |
m_pEyeWidget |
kIQ, true |
源码为 I/Q 时域(非经典眼图) |
| 颜色 | 条件 | 含义 |
|---|---|---|
| 黄色 | _input == true |
星座:符号间 I-Q 轨迹折线;眼图:折叠叠加的黄色迹线(eyeInPen) |
| 红色 | _output == true(SystemControl/EyeDiagram 版) |
星座:各 (I,Q) 位置 4×4 红色像素块(参考/输出点) |
3.4 星座图绘制步骤

模式 : _isDrawConstellation=true,走 DrawConstell。
- DrawBGLayer:黑底、I/Q 网格、±1 刻度、中心十字线。
- 坐标映射 (每点 i):
screenX = 左边距 + 中心X + I × gainscreenY = 上边距 + 中心Y + Q × gain(非正方形控件做等比例补偿)- 超界裁剪。
- _input=true:黄线连接各点(≤4096 点平滑曲线,否则折线)→ 符号跳转轨迹。
- _output=true:在 (I,Q) 处画点(红块或浅绿像素,取决于实现版本)。
- DrawTicks:轴标签。
- paintEvent:合成三层 QImage。
64QAM:ModFormat 只影响解调算法;UI 只接收已解调的 I/Q 序列,理想时呈 8×8 点阵,噪声导致扩散。
3.5 眼图绘制步骤

- X = 样本序号(时间);I 路、Q 路分别为绿/红折线(若
_isShowIPart/_isShowQPart开启)。 - 不是经典折叠眼图。
经典眼图
模式 :_isDrawEye=true → DrawEyediagram。
period = _samplePerSymbol × 2(2 符号周期宽度)。- 按
period分段取连续 IQ 点,X = 段内时间槽,Y =中心 + I×gain。 - 每段画一条黄色 折线(
eyeInPen),多段叠加成「眼」。 _output=true时另有绿色输出迹线(通常未开,故界面只见黄色)。
数据要求 :高采样率时域 IQ(meaDatI/Q);当前 VSA 传的是稀疏 I_constellation(每符号 1 点),更适合星座图。
3.6 后台线程刷新
DrawThread::run(约 100ms 周期):
_isDrawBGLayer→ 重画网格。_iqDataChanged→ 拷贝 IQ →DrawFGLayer→ 更新前景。_isDrawFGLayer→ resize/切模式时强制重画。_isClickedMouse→ 点击读数框。
3.7 VSA 当前代码缺口
| 环节 | 状态 |
|---|---|
| 测量 → MainWindow | 正常 |
| Complex 合成 + Render | 已注释(ReceiveData 265--298 行) |
| SetSampleRate / SetSamplePerSymbol | 已注释 |
| m_pEyeWidget 模式 | 配置为 kIQ,非 kEye |
完整可参考 Plugins/UIFIXAQPlugin 中 _eyeDiagram->Render() 调用链。
第四部分:两套体系统对照
| 维度 | RTSA | VSA |
|---|---|---|
| 宿主 | TraceView + GraphItemBase | 独立 QWidget + QImage 贴图 |
| 主数据 | Trace1 + BitMap(256×N) | kIData + kQData → Complex[] |
| 颜色系统 | RtsaPublicFunc 6 套色温 + GetSelectColor | 固定黄/红(input/output) |
| 异步 | TraceView 重绘回调 paint | DrawThread 100ms 循环 |
| 默认视图 | Base_Thick(DisplayType=4) | kConsole + kIQ |