You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解调过零点调幅信号?三菱VisiTel图像信号解码问询

解码三菱VisiTel过零点调幅信号的数学方法

首先得明确:你遇到的这种调制不是普通的调幅(AM),而是过零点极性反转调制——本质是一种简化的相位键控(BPSK),信息通过载波极性的翻转(过零点时发生)来传递,而非幅度变化,这就是普通包络/乘积检波器失效的原因。结合你提到的信号参数(1764Hz载波、44.1kHz采样、~25样本周期),给你一套可落地的数学解调方案:

1. 先解决载波同步问题(处理±1样本偏移)

因为实际采样和载波可能存在微小的频率偏差,导致周期有±1样本波动,第一步要锁定载波的平均周期和相位:

  • 过零点统计法:遍历采样序列,统计相邻过零点的间隔,计算平均周期,用来校准后续的采样窗口:
int zeroCrossCount = 0;
long long totalInterval = 0;
int lastCrossPos = 0;
bool lastSign = (samples[0] > 0);

for (int i = 1; i < sampleCount; ++i) {
    bool currSign = (samples[i] > 0);
    if (currSign != lastSign) {
        if (zeroCrossCount > 0) {
            totalInterval += i - lastCrossPos;
        }
        lastCrossPos = i;
        zeroCrossCount++;
        lastSign = currSign;
    }
}
float avgCycleSamples = static_cast<float>(totalInterval) / zeroCrossCount;
  • 进阶可以用**数字锁相环(DPLL)**实时跟踪相位偏移,不过对于VisiTel这种稳定的信号,上面的统计法足够应付±1样本的波动。

2. 核心:极性反转检测(解调信息)

既然信息藏在载波极性的翻转里,我们需要跟踪极性状态,并检测翻转时刻:

方法一:差分极性采样

利用平均周期,每隔avgCycleSamples个样本取一个采样值,比较当前和前一个周期的极性——如果相反,说明发生了有效翻转(对应像素明暗切换):

bool currPolarity = (samples[0] > 0);
int pos = static_cast<int>(round(avgCycleSamples));

while (pos < sampleCount) {
    bool nextPolarity = (samples[pos] > 0);
    if (nextPolarity != currPolarity) {
        // 标记此处为像素边界,切换当前像素状态
        togglePixelState(pos);
        currPolarity = nextPolarity;
    }
    // 根据行扫描周期,更新当前像素的位置
    updatePixelPosition(pos);
    pos += static_cast<int>(round(avgCycleSamples));
}

方法二:希尔伯特变换提取瞬时相位(更严谨)

用希尔伯特变换得到信号的解析形式,计算瞬时相位,相位跳变(±π弧度)就是极性翻转的时刻:

// 假设samples是float类型的输入数组,长度为N
cv::Mat input(N, 1, CV_32F, samples);
cv::Mat hilbert;
cv::dft(input, hilbert, cv::DFT_COMPLEX_OUTPUT);

// 构造希尔伯特变换的频域滤波器
cv::Mat filter(N, 1, CV_32FC2);
for (int i = 0; i < N; ++i) {
    if (i == 0 || i == N/2) {
        filter.at<cv::Vec2f>(i) = {1.0f, 0.0f};
    } else if (i < N/2) {
        filter.at<cv::Vec2f>(i) = {1.0f, 1.0f};
    } else {
        filter.at<cv::Vec2f>(i) = {1.0f, -1.0f};
    }
}

cv::mulSpectrums(hilbert, filter, hilbert, 0);
cv::dft(hilbert, hilbert, cv::DFT_INVERSE | cv::DFT_SCALE);

// 计算瞬时相位并检测跳变
const float eps = 0.1f;
float lastPhase = atan2(hilbert.at<cv::Vec2f>(0)[1], hilbert.at<cv::Vec2f>(0)[0]);

for (int i = 1; i < N; ++i) {
    float currPhase = atan2(hilbert.at<cv::Vec2f>(i)[1], hilbert.at<cv::Vec2f>(i)[0]);
    float phaseDiff = currPhase - lastPhase;
    
    // 处理相位卷绕(-π到π的循环)
    if (phaseDiff > M_PI) phaseDiff -= 2*M_PI;
    if (phaseDiff < -M_PI) phaseDiff += 2*M_PI;
    
    if (fabs(phaseDiff) > M_PI - eps) {
        // 检测到极性翻转,标记像素边界
        togglePixelState(i);
    }
    lastPhase = currPhase;
}

3. 修复暗区反转问题

你之前遇到的暗区反转,是因为状态机误判了噪声或单次相位波动导致的假翻转。解决办法:

  • 给状态机加防抖逻辑:只有连续检测到2次以上的极性反转(或连续2个载波周期的极性都和之前相反),才判定为有效状态切换
  • 提前校准极性和明暗的对应关系:比如先采集一段已知的亮/暗区域信号,确定“正极性对应亮”还是“负极性对应亮”

关键总结

这种过零点调制的核心是跟踪载波相位/极性的变化,而非提取幅度包络。上面的两种方法都能有效解决相位偏移、暗区反转的问题,其中差分极性采样实现简单,适合嵌入式或实时场景;希尔伯特变换则更严谨,适合处理噪声较大或频率偏移明显的信号。

内容的提问来源于stack exchange,提问作者SoftEgg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:11:25