离题:让我先说java对我来说是全新的。我已经编程超过15年了,除了修改别人的代码库之外,从来没有其他的需要,所以请原谅我的无知和可能不恰当的术语。我也不是很熟悉射频,所以如果我是左场在这里,请让我知道!
我正在建立一个sdr(软件无线电)无线电发射器,虽然我可以成功地在一个频率上传输,但当我发送流(无论是从设备的麦克风还是从音调发生器的字节)时,通过我的手持接收器传来的声音听起来像是静态的。
我认为这是因为我的接收器被设置为接收nfm(窄带频率调制)和wfm(宽带频率调制),而来自我的sdr的传输是发送原始的、未调制的数据。
我的问题是:如何调制音频字节(即输入流),使产生的字节以fm(频率调制)或am(幅度调制)调制,然后我可以通过sdr传输?
我似乎找不到处理调制的类或包(最终我将不得不调制wfm、fm、am、sb、lsb、usb、dsb等),尽管有相当多的开源sdr代码基,但如果你知道我在哪里可以找到这个,这基本上回答了这个问题。到目前为止,我所发现的一切都已经过去了。
这是我围绕xarph在stackoverflow上的答案建立的一个类,它只返回一个字节数组,其中包含一个简单的未调制音频信号,然后可以通过扬声器播放声音(或者通过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;
}
}
解决这个问题的答案不一定是代码,实际上是理论,当涉及到处理字节数组并将其转换为正确的格式时,了解调频或调幅调制是如何工作的可能会更有价值,因为我将来必须实现更多的模式。
1条答案
按热度按时间axr492tv1#
关于收音机我有很多不懂的东西。但我想我可以说一些关于调制的基本知识和手头的问题,考虑到我所拥有的少量物理知识和编写调频合成器的经验。
首先,我认为如果将源信号的pcm数据点转换为标准化浮点值(范围从-1f到1f),而不是使用短浮点值,您可能会发现使用这些数据点更容易。
接收器的目标频率,510-1700千赫(调幅无线电)明显快于源声的采样率(大概44.1khz)。假设您有办法输出结果数据,数学将涉及从您的信号中提取pcm值,适当地缩放它(idk多少),并将该值乘以您的载波信号生成的pcm数据点,该点对应于时间间隔。
例如,如果载波信号为882 khz,则在转到下一个源信号值之前,将20个载波信号值与源信号值相乘。同样,我的无知:这项技术可能有某种平滑算法,用于源信号数据点之间的转换。我真的不知道这是不是,也不知道发生在什么阶段。
对于调频,我们有兆赫范围内的载波信号,因此我们谈论的是每个源信号值产生的数据比调幅要多几个数量级。我不知道确切的算法使用,但这里有一个简单的概念方法来实现频率调制的正弦,我用我的调频合成器。
假设您有一个包含1000个数据点的表,它表示一个范围在-1f到1f之间的正弦波。假设您有一个重复遍历表的游标。如果光标以44100 fps的速度精确前进1个数据点并以该速率传递值,则生成的音调将为44.1 hz,是吗?但也可以通过大于1的间隔遍历表,例如1.5。当光标落在两个表值之间时,可以使用线性插值来确定要输出的值。光标增量为1.5将导致正弦波以66.2 hz的频率倾斜。
我认为fm所发生的是光标的增量是连续变化的,它的变化量取决于从源信号转换成增量范围的某种缩放。
缩放的细节我不知道。但是,假设一个信号是用10mhz的载波传输的,其范围约为1%(大约从9.9 mhz到10.1 mhz),归一化源信号将具有某种算法,其中-1的pcm值匹配穿过载波的增量,使其产生较慢的频率,+1匹配穿过载波的增量,使其产生较高的频率。因此,如果+1的增量提供10 mhz,那么-1的源波pcm信号可能会引发+0.99的光标增量,-0.5的pcm值引发+0.995的增量,+0.5的值引发+1.005的增量,+1的值引发1.01的光标增量。
这是纯粹的猜测,我的一部分之间的关系,源pcm值和如何被用来调制载波频率。但也许这有助于给人一个基本机制的具体形象?
(我使用类似的方法,在audiocue(一个用于播放基于java的音频数据的类)中,使用光标以任意增量在wav-pcm数据点上迭代
Clip
),用于实时频移。代码行1183保存在从wav文件导入的pcm数据上迭代的游标,变量idx保存游标增量。第1317行是递增光标后获取音频值的位置。代码行1372具有执行线性插值的readFrame()方法。还实现了实时的卷更改,我对公共输入挂钩提供的值使用平滑处理。)同样,如果在源信号值之间使用了任何类型的平滑,则返回idk。根据我的经验,很多技术都涉及到过滤和其他各种各样的技巧,以提高保真度或处理计算。