【问题标题】:How do I modulate a signal for radio transmission (SDR) in Java?如何在 Java 中调制用于无线电传输 (SDR) 的信号?
【发布时间】:2021-02-27 04:45:01
【问题描述】:

题外话:首先让我说 Java 对我来说是全新的。我已经编程超过 15 年了,除了修改其他人的代码库之外,从来没有需要它,所以请原谅我的无知和可能不恰当的术语。我对射频也不是很熟悉,所以如果我在这里很左,请告诉我!

我正在构建一个 SDR(软件定义无线电)无线电发射器,虽然我可以在某个频率上成功传输,但当我发送流(来自设备的麦克风或来自 tone generator 的字节)时,通过我的手持接收器发出的声音听起来像是静止的

我认为这是因为我的接收器设置为接收 NFM(窄带频率调制)和 WFM(宽带频率调制),而 来自我的 SDR 的传输正在发送原始的、未调制的数据 .

我的问题是:如何调制音频字节(即 InputStream),以便在 FM(频率调制)AM 中调制结果字节(调幅),然后我可以通过 SDR 传输?

我似乎找不到处理调制的类或包(最终我将不得不调制 WFM、FM、AM、SB、LSB、USB、DSB 等),尽管有很多开源 SDR 代码库,但如果你知道我在哪里可以找到它,那基本上就回答了这个问题。到目前为止,我发现的所有内容都用于解调。

这是我在 StackOverflow 上围绕 Xarph's Answer 构建的一个类,它只返回一个字节数组,其中包含一个简单的未调制音频信号,然后可用于通过扬声器播放声音(或通过 SDR 传输,但由于结果没有被正确调制,它没有在接收器端正确通过,这是我无法弄清楚的)

public class ToneGenerator {

    public static byte[] generateTone() {
        return generateTone(60, 1000, 8000);
    }

    public static byte[] generateTone(double duration) {
        return generateTone(duration, 1000, 8000);
    }

    public static byte[] generateTone(double duration, double freqOfTone) {
        return generateTone(duration, freqOfTone, 8000);
    }

    public static byte[] generateTone(double duration, double freqOfTone, int sampleRate) {
        double dnumSamples = duration * sampleRate;
        dnumSamples = Math.ceil(dnumSamples);
        int numSamples = (int) dnumSamples;
        double sample[] = new double[numSamples];
        byte generatedSnd[] = new byte[2 * numSamples];


        for (int i = 0; i < numSamples; ++i) {    // Fill the sample array
            sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate));
        }

        // convert to 16 bit pcm sound array
        // assumes the sample buffer is normalized.
        // convert to 16 bit pcm sound array
        // assumes the sample buffer is normalised.
        int idx = 0;
        int i = 0 ;

        int ramp = numSamples / 20 ;                                     // Amplitude ramp as a percent of sample count


        for (i = 0; i< ramp; ++i) {                                      // Ramp amplitude up (to avoid clicks)
            double dVal = sample[i];
            // Ramp up to maximum
            final short val = (short) ((dVal * 32767 * i/ramp));
            // in 16 bit wav PCM, first byte is the low order byte
            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
        }


        for (i = i; i< numSamples - ramp; ++i) {                         // Max amplitude for most of the samples
            double dVal = sample[i];
            // scale to maximum amplitude
            final short val = (short) ((dVal * 32767));
            // in 16 bit wav PCM, first byte is the low order byte
            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
        }

        for (i = i; i< numSamples; ++i) {                                // Ramp amplitude down
            double dVal = sample[i];
            // Ramp down to zero
            final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp ));
            // in 16 bit wav PCM, first byte is the low order byte
            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
        }

        return generatedSnd;
    }
}

这个问题的答案不一定是代码,实际上是理论和理解 FM 或 AM 调制在处理字节数组并将其转换为正确格式时的工作原理可能更有价值,因为我未来将不得不实现更多模式。

【问题讨论】:

  • 您可能需要硬件来调制射频频率。
  • 这是一个很好的观点,我没有考虑检查 HackRF 库以查看它是否在处理调制硬件端(我认为它是软件,因为 SDR 控制台会这样做,但那是为了接收,有除了 SDR Angel 外,在传输方面没有太多可用的) - 会调查一下,谢谢!

标签: java audio modulation software-defined-radio


【解决方案1】:

关于收音机,我有很多不知道的地方。但我想我可以谈谈调制的基础知识和手头的问题,因为我有一点物理知识和编写 FM 合成器的经验。

首先,我认为如果您将源信号的 PCM 数据点转换为标准化浮点数(范围从 -1f 到 1f),而不是使用短裤,您可能会发现使用它们会更容易。

接收器的目标频率 510-1700 kHz(AM 收音机)明显快于源声音的采样率(大概是 44.1kHz)。假设您有办法输出结果数据,数学运算将涉及从您的信号中获取一个 PCM 值,适当地对其进行缩放(IDK 多少)并将该值与您的载波信号生成的对应于时间的 PCM 数据点相乘间隔。

例如,如果载波信号为 882 kHz,您需要将 20 个载波信号值的序列与源信号值相乘,然后再转到下一个源信号值。再说一次,我的无知:该技术可能有某种平滑算法用于源信号数据点之间的转换。我真的不知道这件事与否,也不知道它发生在什么阶段。

对于 FM,我们的载波信号在 MHz 范围内,因此每个源信号值生成的数据比 AM 多几个数量级。我不知道使用的确切算法,但这里有一个简单的概念方法来实现我与 FM 合成器一起使用的正弦的频率调制。

假设您有一个包含 1000 个数据点的表格,这些数据点代表一个范围在 -1f 到 1f 之间的正弦波。假设您有一个反复遍历表的游标。如果光标以 44100 fps 恰好前进 1 个数据点并以该速率传递值,则生成的音调将为 44.1 Hz,是吗?但您也可以通过大于 1 的间隔(例如 1.5)遍历表。当光标落在两个表值之间时,可以使用线性插值来确定要输出的值。 1.5 的光标增量将导致正弦波的音调为 66.2 Hz。

我认为 FM 发生的情况是,这个光标增量是连续变化的,它的变化量取决于从源信号转换为增量范围的某种缩放。

我不知道缩放的细节。但是假设一个信号正在以 10MHz 的载波传输,范围约为 1%(大约从 9.9MHz 到 10.1MHz),归一化的源信号将具有某种算法,其中 PCM 值为 -1 匹配一个增量,该增量穿过载波使其产生较低的频率,+1 匹配穿过载波的增量,使其产生较高的频率。因此,如果 +1 的增量提供 10 MHz,则可能 -1 的源波 PCM 信号会引发 +0.99 的光标增量,-0.5 的 PCM 值会引发 +0.995 的增量,+0.5 的值会引发增量+1.005,+1 的值会导致光标增量 1.01。

这纯粹是我对源 PCM 值之间的关系以及如何使用它来调制载波频率的推测。但也许它有助于给出基本机制的具体图像?

(我使用类似的东西,使用光标以任意增量迭代 wav PCM 数据点,在 AudioCue(基于 Java Clip 的用于播放音频数据的类),用于实时频移。代码第 1183 行保存了遍历从 wav 文件导入的 PCM 数据的光标,变量 idx 保存了光标增量。第 1317 行是我们在增加光标后获取音频值的地方. 代码行 1372 具有执行线性插值的方法 readFractionalFrame()。还实现了实时音量变化,并且我对从公共输入挂钩提供的值进行了平滑处理。)

同样,IDK 是否在源信号值之间使用任何类型的平滑。根据我的经验,很多技术都涉及过滤和其他各种技巧,以提高保真度或处理计算。

【讨论】:

  • 这个答案给我留下了更多的问题(在这种情况下这是一件非常好的事情 - 以前,我什至不知道该问什么),但也知道如何解决我的问题。非常感谢您非常全面的回复!我绝对觉得这些信息(以及听到您如何完成类似的事情,这表明我认为这一切都是错误的)很可能会让我正确地弄清楚这一点。这完美地回答了这个问题,因为我更关注理论而不是代码,所以谢谢你!
  • 很高兴为您提供帮助!很高兴知道我至少在一天中的一部分时间做了一些有价值的事情。是的,我还想了解更多关于无线电的信息。同时,关于音频信号处理的一个很好的(免费)资源是dspguide.com“DSP 的科学家和工程师指南”,您还可以考虑在dsp.stackexchange.com 是否可以更好地回答您的问题。如果您还有其他特定于 java 的问题,我会每天关注 javasound 标签。
  • 非常感谢您将我指向那本书并让我了解其他 Stack Exchange 平台!这两个看起来都很有用,我不知道有这样的东西。我注意到除了不能很好地工作的示例代码之外,Android 上的 HackRF 缺少发射器,所以我希望我可以制作一些类似于 PortaPack 但适用于 Android 的东西。除了 SDR Angel 之外,Windows 在 TX 方面也缺乏很多。肯定有很多东西要学,这就像将编程和广播社区联系在一起,让事情变得有趣。
猜你喜欢
  • 2016-11-09
  • 1970-01-01
  • 2019-08-18
  • 2016-05-26
  • 1970-01-01
  • 1970-01-01
  • 2011-09-12
  • 2017-02-24
  • 2012-12-05
相关资源
最近更新 更多