接下来我要讲一个高难度应用,SSTV接收机。
很多ham都喜欢玩sstv,一般方式都是用一个对讲机来接收地面波或者卫星发出的信号,然后用手机录制对讲机发出来的声音,再把这个录音放到电脑旁边播放来解调(也有用对讲机收,然后手机直接解调的)。这样做太麻烦。我想尝试用portapack直接接收并解调图片。hackrf虽然灵敏度不高,但是接收并不像发射那样跟对讲机有那么大的差距,所以只要信号够强,应该没什么问题。
而且portapack目前其实已经有些现成的sstv发射app了,虽然制式还不是很全,但是我测试过,确实可以用。 所以从处理能力上来说portapack应该是没什么问题的。
现在有几个现成软件,我打算参考一下。一个是slowrx,另一个是robot36。
slowrx支持多种模式的解调,可以直接从alsa(声卡麦克风)中解调出sstv图像,我把它与我自己的hackrf_fm解调软件共同使用,然后用Portapack发射,成功实现了实时解调(不经过wav录制)。但是slowrx有个缺点,它过于复杂,不利于直接更改,只能把它当做参考或者测试工具。
robot36,相比之下简洁很多,它支持编码和解码,可以把一个图片编码成wav文件,也开以从wav解码出图片,这里wav文件也能用alsa声卡替换,实现实时发射和接收(基带信号)。但是有一个问题,这个robot36格式不被portapack支持,只被slowrx支持。
所以我接下来的开发计划是:
1.参考slowrx的配置文件,把robot36的编码部分的制式从robot36改为scottie s2,然后用robot36的编码部分发给slowrx,看看scottie s2模式下是否还能成功解码。
2.更改robot36的解码部分,看看它能否成功解码scottie s2模式下的robot36编码程序。
3.把robot36的解码部分和我以前写的hackrf_fm连起来,用这一套程序在电脑上实时解调portapack发出的scottie s2的图像信号。
4.把robot36的解码程序+hackrf_fm解调程序移植到portapack中,实现我要的app。
这个app工作量不小,但是实现以后对我们将来解调noaa应该有不小的帮助。
后来我发现这个计划不行,robot36的希尔伯特变换解调起来太难了。
我的真实做法是:
1.学习pysstv等几个发射程序了解sstv的真实原理,以及各模式的区别。
2.学习俄国人的java程序。并用python改写。期间我先试实现了vis解码,再实现了图像解码。
3.参考robot36的解码程序,尤其是wav和alsa读取部分,以robot36位总体框架,把上一步的python或者java解码程序搬进这个c程序。
4.把c程序改为c++程序,再跟hackrf解调fm程序对接。实现实时解调无线电信号中的图像(不经过音频了)。
https://github.com/colaclanth/sstv python decoder
https://github.com/dnet/pySSTV python encoder
https://github.com/Oros42/SSTV_Robot_encoder c++ encoder (modified from pySSTV)
https://github.com/MossFrog/SSTV-Decoder python decoder (hilbert)
https://github.com/brainwagon/sstv-encoders c++ encoder
其他参考:
https://www.docin.com/p-1742864375.html
http://dp.nonoo.hu/projects/ham-dsp-tutorial/ java encoder decoder(filter/fft/complex)
http://www.spetzler.dk/bjarke/Projects/Dsp/DSP%20FM%20Demodulator%20for%20SSTV.htm
https://github.com/MossFrog/SSTV-Decoder python decoder (hilbert)
SSTV-complete是一个很简单的decoder。我正打算把SSTVDecoder.java "fmDemodulateLuminance"移植过去。
// 800Hz, beta 0.5, impulse length 50
private double[] FIRLPCoeffs = { +0.0001082461, +0.0034041195, +0.0063570207, +0.0078081648,
+0.0060550614, -0.0002142384, -0.0104500335, -0.0211855480,
-0.0264527776, -0.0201269304, +0.0004419626, +0.0312014771,
+0.0606261038, +0.0727491887, +0.0537028370, -0.0004362161,
-0.0779387981, -0.1511168919, -0.1829049634, -0.1390189257,
-0.0017097774, +0.2201896764, +0.4894395006, +0.7485289338,
+0.9357596142, +1.0040320616, +0.9357596142, +0.7485289338,
+0.4894395006, +0.2201896764, -0.0017097774, -0.1390189257,
-0.1829049634, -0.1511168919, -0.0779387981, -0.0004362161,
+0.0537028370, +0.0727491887, +0.0606261038, +0.0312014771,
+0.0004419626, -0.0201269304, -0.0264527776, -0.0211855480,
-0.0104500335, -0.0002142384, +0.0060550614, +0.0078081648,
+0.0063570207, +0.0034041195, +0.0001082461,
};
private double[] xvFIRLP1 = new double[FIRLPCoeffs.length];
public double FIRLowPass1(double sampleIn) {
for (int i = 0; i < xvFIRLP1.length-1; i++)
xvFIRLP1[i] = xvFIRLP1[i+1];
xvFIRLP1[xvFIRLP1.length-1] = sampleIn / 5.013665674e+00;
double sum = 0;
for (int i = 0; i <= xvFIRLP1.length-1; i++)
sum += (FIRLPCoeffs[i] * xvFIRLP1[i]);
return sum;
}
private double[] xvFIRLP2 = new double[FIRLPCoeffs.length];
public double FIRLowPass2(double sampleIn) {
for (int i = 0; i < xvFIRLP2.length-1; i++)
xvFIRLP2[i] = xvFIRLP2[i+1];
xvFIRLP2[xvFIRLP2.length-1] = sampleIn / 5.013665674e+00;
double sum = 0;
for (int i = 0; i <= xvFIRLP2.length-1; i++)
sum += (FIRLPCoeffs[i] * xvFIRLP2[i]);
return sum;
}
// moving average
private double[] xvMA1 = new double[9];
private double yvMA1prev = 0;
public double noiseReductionFilter1(double sampleIn) {
for (int i = 0; i < xvMA1.length-1; i++)
xvMA1[i] = xvMA1[i+1];
xvMA1[xvMA1.length-1] = sampleIn;
yvMA1prev = yvMA1prev+xvMA1[xvMA1.length-1]-xvMA1[0];
return yvMA1prev;
}
private double[] xvMA2 = new double[xvMA1.length];
private double yvMA2prev = 0;
public double noiseReductionFilter2(double sampleIn) {
for (int i = 0; i < xvMA2.length-1; i++)
xvMA2[i] = xvMA2[i+1];
xvMA2[xvMA2.length-1] = sampleIn;
yvMA2prev = yvMA2prev+xvMA2[xvMA2.length-1]-xvMA2[0];
return yvMA2prev;
}
//private double fmDemodMinVal = 0, fmDemodMaxVal = 0;
private double oscPhase = 0;
private double realPartPrev = 0, imaginaryPartPrev = 0;
public int fmDemodulateLuminance(double sample) {
oscPhase += (2 * Math.PI * 2000) / SAMPLERATE;
double realPart = Math.cos(oscPhase) * sample;
double imaginaryPart = Math.sin(oscPhase) * sample;
if (oscPhase >= 2 * Math.PI)
oscPhase -= 2 * Math.PI;
realPart = FIRLowPass1(realPart);
imaginaryPart = FIRLowPass2(imaginaryPart);
realPart = noiseReductionFilter1(realPart);
imaginaryPart = noiseReductionFilter2(imaginaryPart);
sample = (imaginaryPart*realPartPrev-realPart*imaginaryPartPrev)/(realPart*realPart+imaginaryPart*imaginaryPart);
realPartPrev = realPart;
imaginaryPartPrev = imaginaryPart;
sample += 0.2335; // bring the value above 0
wavFM.write(getBytesFromDouble(sample), 0, 2);
int luminance = (int)Math.round((sample/0.617)*255);
luminance = 255-luminance;
//System.out.println("lum: " + luminance);
if (luminance > 255)
luminance = 255;
if (luminance < 0)
luminance = 0;
return luminance;
}
移植过去以后发现还是解调不出来,问题很多。
我打算把那个java程序简化一下,只留一下vis解码部分,然后改写为python代码试试。
以下两段代码就是我实现的vis解码,实测都是可以运行的。它们都是先探测了header,也就是1900Hz->1200Hz->1900Hz这个过程,然后对vis的几个bit(也是通过判断音调高低)进行了解码得到的。传输一个bit的持续的时间长度是30ms,计算的时候用的是这个范围里的最后一个点对应的音调频率,作为bit的值,没有取这30ms的平均值。
java decode vis:
package com.nonoo.sstvdecoder;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SSTVDecoder extends JPanel implements Runnable {
private final static int SAMPLERATE = 8000;
private final static int BUFFERSIZE = SAMPLERATE;
private final static String inputWAVFileName = "mmsstv.wav";
private int count = 0;
private double oneBitSampleCount;
AudioInputStream audioInputStream;
public SSTVDecoder() {
try {
audioInputStream = AudioSystem.getAudioInputStream(new File(inputWAVFileName));
} catch (Exception e) {
e.printStackTrace();
}
}
// this function returns a sample from the sound device
public double getSample() {
int bytesRead;
byte[] sample = new byte[2];
double sampleDouble = 0;
try {
do {
bytesRead = audioInputStream.read(sample, 0, 2); // reading from the input WAV file instead
} while (bytesRead < 2);
// converting frame stored as bytes to double
sampleDouble = ((sample[0] & 0xFF) | (sample[1] << 8)) / 32768.0;
//System.out.println("sampleDouble: " + sampleDouble + " sample[0]: " + sample[0] + " sample[1]: " + sample[1] + " count" + count);
count = count + 1;
// max count is 940780
} catch (Exception e) {
}
return bandPassSSTVSignal(sampleDouble);
}
// for filter designing, see http://www-users.cs.york.ac.uk/~fisher/mkfilter/
// order 2 Butterworth, freqs: 1100-2300 Hz
private double[] xvBPV = new double[5], yvBPV = new double[5];
public double bandPassSSTVSignal(double sampleIn) {
xvBPV[0] = xvBPV[1]; xvBPV[1] = xvBPV[2]; xvBPV[2] = xvBPV[3]; xvBPV[3] = xvBPV[4];
xvBPV[4] = sampleIn / 7.627348301e+00;
yvBPV[0] = yvBPV[1]; yvBPV[1] = yvBPV[2]; yvBPV[2] = yvBPV[3]; yvBPV[3] = yvBPV[4];
yvBPV[4] = (xvBPV[0] + xvBPV[4]) - 2 * xvBPV[2]
+ ( -0.2722149379 * yvBPV[0]) + ( 0.3385637917 * yvBPV[1])
+ ( -0.8864523063 * yvBPV[2]) + ( 0.7199258671 * yvBPV[3]);
return yvBPV[4];
}
// order 2 Butterworth, freqs: 1050-1150 Hz
private double[] xvBP0 = new double[5], yvBP0 = new double[5];
public double bandPassFreq0(double sampleIn) {
xvBP0[0] = xvBP0[1]; xvBP0[1] = xvBP0[2]; xvBP0[2] = xvBP0[3]; xvBP0[3] = xvBP0[4];
xvBP0[4] = sampleIn / 6.847831231e+02;
yvBP0[0] = yvBP0[1]; yvBP0[1] = yvBP0[2]; yvBP0[2] = yvBP0[3]; yvBP0[3] = yvBP0[4];
yvBP0[4] = (xvBP0[0] + xvBP0[4]) - 2 * xvBP0[2]
+ ( -0.8948743446 * yvBP0[0]) + ( 2.3910210304 * yvBP0[1])
+ ( -3.4874837696 * yvBP0[2]) + ( 2.5276736881 * yvBP0[3]);
return yvBP0[4];
}
// order 2 Butterworth, freq 50 Hz
private double[] xvLP0 = new double[5], yvLP0 = new double[3];
public double lowPass0(double sampleIn) {
xvLP0[0] = xvLP0[1]; xvLP0[1] = xvLP0[2];
xvLP0[2] = sampleIn / 2.666171709e+03;
yvLP0[0] = yvLP0[1]; yvLP0[1] = yvLP0[2];
yvLP0[2] = (xvLP0[0] + xvLP0[2]) + 2 * xvLP0[1]
+ ( -0.9459779362 * yvLP0[0]) + ( 1.9444776578 * yvLP0[1]);
return yvLP0[2];
}
// order 2 Butterworth, freqs: 1150-1250 Hz
private double[] xvBP1 = new double[5], yvBP1 = new double[5];
public double bandPassFreq1(double sampleIn) {
xvBP1[0] = xvBP1[1]; xvBP1[1] = xvBP1[2]; xvBP1[2] = xvBP1[3]; xvBP1[3] = xvBP1[4];
xvBP1[4] = sampleIn / 6.847831360e+02;
yvBP1[0] = yvBP1[1]; yvBP1[1] = yvBP1[2]; yvBP1[2] = yvBP1[3]; yvBP1[3] = yvBP1[4];
yvBP1[4] = (xvBP1[0] + xvBP1[4]) - 2 * xvBP1[2]
+ ( -0.8948743446 * yvBP1[0]) + ( 2.1640020371 * yvBP1[1])
+ ( -3.1983590493 * yvBP1[2]) + ( 2.2876800081 * yvBP1[3]);
return yvBP1[4];
}
// order 2 Butterworth, freq 50 Hz
private double[] xvLP1 = new double[5], yvLP1 = new double[3];
public double lowPass1(double sampleIn) {
xvLP1[0] = xvLP1[1]; xvLP1[1] = xvLP1[2];
xvLP1[2] = sampleIn / 2.666171709e+03;
yvLP1[0] = yvLP1[1]; yvLP1[1] = yvLP1[2];
yvLP1[2] = (xvLP1[0] + xvLP1[2]) + 2 * xvLP1[1]
+ ( -0.9459779362 * yvLP1[0]) + ( 1.9444776578 * yvLP1[1]);
return yvLP1[2];
}
// order 2 Butterworth, freqs: 1250-1350 Hz
private double[] xvBP2 = new double[5], yvBP2 = new double[5];
public double bandPassFreq2(double sampleIn) {
xvBP2[0] = xvBP2[1]; xvBP2[1] = xvBP2[2]; xvBP2[2] = xvBP2[3]; xvBP2[3] = xvBP2[4];
xvBP2[4] = sampleIn / 6.847831430e+02;
yvBP2[0] = yvBP2[1]; yvBP2[1] = yvBP2[2]; yvBP2[2] = yvBP2[3]; yvBP2[3] = yvBP2[4];
yvBP2[4] = (xvBP2[0] + xvBP2[4]) - 2 * xvBP2[2]
+ ( -0.8948743446 * yvBP2[0]) + ( 1.9236412517 * yvBP2[1])
+ ( -2.9236524734 * yvBP2[2]) + ( 2.0335820202 * yvBP2[3]);
return yvBP2[4];
}
// order 2 Butterworth, freq 50 Hz
private double[] xvLP2 = new double[5], yvLP2 = new double[3];
public double lowPass2(double sampleIn) {
xvLP2[0] = xvLP2[1]; xvLP2[1] = xvLP2[2];
xvLP2[2] = sampleIn / 2.666171709e+03;
yvLP2[0] = yvLP2[1]; yvLP2[1] = yvLP2[2];
yvLP2[2] = (xvLP2[0] + xvLP2[2]) + 2 * xvLP2[1]
+ ( -0.9459779362 * yvLP2[0]) + ( 1.9444776578 * yvLP2[1]);
return yvLP2[2];
}
// order 2 Butterworth, freqs: 1450-1550 Hz
private double[] xvBP3 = new double[5], yvBP3 = new double[5];
public double bandPassFreq3(double sampleIn) {
xvBP3[0] = xvBP3[1]; xvBP3[1] = xvBP3[2]; xvBP3[2] = xvBP3[3]; xvBP3[3] = xvBP3[4];
xvBP3[4] = sampleIn / 6.847831487e+02;
yvBP3[0] = yvBP3[1]; yvBP3[1] = yvBP3[2]; yvBP3[2] = yvBP3[3]; yvBP3[3] = yvBP3[4];
yvBP3[4] = (xvBP3[0] + xvBP3[4]) - 2 * xvBP3[2]
+ ( -0.8948743446 * yvBP3[0]) + ( 1.4088950412 * yvBP3[1])
+ ( -2.4440289254 * yvBP3[2]) + ( 1.4894168138 * yvBP3[3]);
return yvBP3[4];
}
// order 2 Butterworth, freq 50 Hz
private double[] xvLP3 = new double[5], yvLP3 = new double[3];
public double lowPass3(double sampleIn) {
xvLP3[0] = xvLP3[1]; xvLP3[1] = xvLP3[2];
xvLP3[2] = sampleIn / 2.666171709e+03;
yvLP3[0] = yvLP3[1]; yvLP3[1] = yvLP3[2];
yvLP3[2] = (xvLP3[0] + xvLP3[2]) + 2 * xvLP3[1]
+ ( -0.9459779362 * yvLP3[0]) + ( 1.9444776578 * yvLP3[1]);
return yvLP3[2];
}
// order 2 Butterworth, freqs: 1850-1950 Hz
private double[] xvBP4 = new double[5], yvBP4 = new double[5];
public double bandPassFreq4(double sampleIn) {
xvBP4[0] = xvBP4[1]; xvBP4[1] = xvBP4[2]; xvBP4[2] = xvBP4[3]; xvBP4[3] = xvBP4[4];
xvBP4[4] = sampleIn / 6.847831502e+02;
yvBP4[0] = yvBP4[1]; yvBP4[1] = yvBP4[2]; yvBP4[2] = yvBP4[3]; yvBP4[3] = yvBP4[4];
yvBP4[4] = (xvBP4[0] + xvBP4[4]) - 2 * xvBP4[2]
+ ( -0.8948743446 * yvBP4[0]) + ( 0.2888565889 * yvBP4[1])
+ ( -1.9123621269 * yvBP4[2]) + ( 0.3053654444 * yvBP4[3]);
return yvBP4[4];
}
// order 2 Butterworth, freq 50 Hz
private double[] xvLP4 = new double[5], yvLP4 = new double[3];
public double lowPass4(double sampleIn) {
xvLP4[0] = xvLP4[1]; xvLP4[1] = xvLP4[2];
xvLP4[2] = sampleIn / 2.666171709e+03;
yvLP4[0] = yvLP4[1]; yvLP4[1] = yvLP4[2];
yvLP4[2] = (xvLP4[0] + xvLP4[2]) + 2 * xvLP4[1]
+ ( -0.9459779362 * yvLP4[0]) + ( 1.9444776578 * yvLP4[1]);
return yvLP4[2];
}
// this function returns the bit value of the current sample
// possible results:
// 1900 Hz -> 4 (leader tone)
// 1500 Hz -> 3 (sync porch, separator pulse)
// 1300 Hz -> 2 (VIS bit 0)
// 1200 Hz -> 1 (break, VIS start&stop bit)
// 1100 Hz -> 0 (VIS bit 1)
public int demodulator(double sample) {
double[] lines = new double[5];
lines[0] = bandPassFreq0(sample);
lines[1] = bandPassFreq1(sample);
lines[2] = bandPassFreq2(sample);
lines[3] = bandPassFreq3(sample);
lines[4] = bandPassFreq4(sample);
// calculating the RMS of the lines (squaring them)
lines[0] *= lines[0];
lines[1] *= lines[1];
lines[2] *= lines[2];
lines[3] *= lines[3];
lines[4] *= lines[4];
// lowpass filtering the lines
lines[0] = lowPass0(lines[0]);
lines[1] = lowPass1(lines[1]);
lines[2] = lowPass2(lines[2]);
lines[3] = lowPass3(lines[3]);
lines[4] = lowPass4(lines[4]);
// deciding which line is the highest and returning it's index
int maxIndex = 0;
double maxVal = lines[0];
for (int i = 1; i < lines.length; i++) {
if (lines[i] > maxVal) {
maxVal = lines[i];
maxIndex = i;
}
}
return maxIndex;
}
// this function returns at the half of a bit with the bit's value
public int getVisBit() {
int val = 0;
for (int i = 0; i < oneBitSampleCount; i++) {
val = demodulator(getSample());
}
return val;
}
public void startVISReceiving() {
int bitResult;
System.out.print("Header received, starting receiving VIS code: ");
oneBitSampleCount = SAMPLERATE*0.030;
// waiting half bit time (15ms)
for (int i = 0; i < oneBitSampleCount/2; i++) {
demodulator(getSample());
}
int VIS = 0;
int parity = 0;
boolean parityError = false;
for (int i = 0; i < 9; i++) {
bitResult = getVisBit();
int bit = bitResult == 0 ? 1 : 0;
switch (i) {
case 7:
if (parity != bit)
parityError = true;
break;
case 8: break; // stop bit
default:
if (bitResult == 0)
VIS |= bit << (i);
parity ^= bit;
System.out.print(bit);
}
}
System.out.println();
System.out.print("VIS: " + VIS);
if (parityError)
System.out.print(" parity ERROR");
else
System.out.print(" parity OK");
if (VIS == 44) // Martin M1 VIS code
System.out.println(" VIS MARTIN OK");
else
System.out.println(" VIS UNKNOWN ERROR");
// waiting to reach the end of the stop bit
for (int i = 0; i < oneBitSampleCount/4+5; i++) {
demodulator(getSample());
}
}
public int[] waitForEdge(int[] result) {
result[0] = result[1];
while (true) {
result[1] = demodulator(getSample());
if (result[0] != result[1])
break;
result[0] = result[1];
};
return result;
}
public void waitForStart() {
int[] result = new int[2];
int edge41Count = 0;
result[0] = result[1] = 0;
while (true) {
result = waitForEdge(result);
// this part starts VIS receiving if we receive 41 -> 14 -> 41 transitions (bits 4141) in sequence
switch (edge41Count) {
case 0:
if (result[0] == 4 && result[1] == 1) {
edge41Count = 1;
System.out.println("Got edge: 41" + " count :" + count);
continue;
}
break;
case 1:
if (result[0] == 1 && result[1] == 4) { // got 41 -> 14
edge41Count = 2;
System.out.println("Got edge: 14" + " count :" + count);
continue;
}
break;
case 2:
if (result[0] == 4 && result[1] == 1) { // got 41 -> 14 -> 41
System.out.println("Got edge: 41 -> 14 -> 41" + " count :" + count);
startVISReceiving();
}
break;
}
edge41Count = 0;
}
}
@Override
public void run() {
}
public static void main(String[] args) {
//AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, SAMPLERATE, 16, 1, 2, SAMPLERATE, false);
// creating the recorder thread from this class' instance
SSTVDecoder sstvDecoder = new SSTVDecoder();
System.out.println("!!!!Recording... press ENTER to stop recording!");
sstvDecoder.waitForStart();
System.out.println("Recording stopped.");
}
}
python deocde vis:
import wave
import numpy as np
from math import sin, cos, pi
#-- Initial Variables
SRate = 8000
oneBitSampleCount = int(SRate*0.030)
#index = 0
count = 0
def getSample():
global count
#sampleDouble = ((str_data[index] & 0xFF) | (str_data[index+1] << 8) ) / 32768.0
#print ("sampleDouble: ", sampleDouble, " sample[0]: ", str_data[index]," sample[1]: ", str_data[index+1],"count: ",count)
#print ("waveData: ", waveData[count], " count: ", count)
sampleDouble = waveData[count]/ 32768.0
#print ("waveData: ", sampleDouble, " count: ", count)
#index = index + 2
count = count + 1
return bandPassSSTVSignal(sampleDouble)
xvBPV = [0 for _ in range(5)]
yvBPV = [0 for _ in range(5)]
def bandPassSSTVSignal(sampleIn):
global xvBPV, yvBPV
xvBPV[0] = xvBPV[1]
xvBPV[1] = xvBPV[2]
xvBPV[2] = xvBPV[3]
xvBPV[3] = xvBPV[4]
xvBPV[4] = sampleIn / 7.627348301
yvBPV[0] = yvBPV[1]
yvBPV[1] = yvBPV[2]
yvBPV[2] = yvBPV[3]
yvBPV[3] = yvBPV[4]
yvBPV[4] = (xvBPV[0]+xvBPV[4])-2*xvBPV[2]+(-0.2722149379*yvBPV[0])+(0.3385637917*yvBPV[1])+(-0.8864523063*yvBPV[2])+(0.7199258671*yvBPV[3])
return yvBPV[4]
xvBP0 = [0 for _ in range(5)]
yvBP0 = [0 for _ in range(5)]
def bandPassFreq0(sampleIn):
global xvBP0, yvBP0
xvBP0[0] = xvBP0[1]
xvBP0[1] = xvBP0[2]
xvBP0[2] = xvBP0[3]
xvBP0[3] = xvBP0[4]
xvBP0[4] = sampleIn / 684.7831231
yvBP0[0] = yvBP0[1]
yvBP0[1] = yvBP0[2]
yvBP0[2] = yvBP0[3]
yvBP0[3] = yvBP0[4]
yvBP0[4] = (xvBP0[0]+xvBP0[4])-2*xvBP0[2]+(-0.8948743446*yvBP0[0])+(2.3910210304*yvBP0[1])+(-3.4874837696*yvBP0[2])+(2.5276736881*yvBP0[3])
return yvBP0[4]
xvLP0 = [0 for _ in range(5)]
yvLP0 = [0 for _ in range(3)]
def lowPass0(sampleIn):
global xvLP0, yvLP0
xvLP0[0] = xvLP0[1]
xvLP0[1] = xvLP0[2];
xvLP0[2] = sampleIn / 2666.171709
yvLP0[0] = yvLP0[1]
yvLP0[1] = yvLP0[2]
yvLP0[2] = (xvLP0[0]+xvLP0[2])+2*xvLP0[1]+(-0.9459779362*yvLP0[0])+(1.9444776578*yvLP0[1])
return yvLP0[2]
xvBP1 = [0 for _ in range(5)]
yvBP1 = [0 for _ in range(5)]
def bandPassFreq1(sampleIn):
global xvBP1, yvBP1
xvBP1[0] = xvBP1[1]
xvBP1[1] = xvBP1[2]
xvBP1[2] = xvBP1[3]
xvBP1[3] = xvBP1[4]
xvBP1[4] = sampleIn / 684.7831360
yvBP1[0] = yvBP1[1]
yvBP1[1] = yvBP1[2]
yvBP1[2] = yvBP1[3]
yvBP1[3] = yvBP1[4]
yvBP1[4] = (xvBP1[0]+xvBP1[4])-2*xvBP1[2]+(-0.8948743446*yvBP1[0])+(2.1640020371*yvBP1[1])+(-3.1983590493*yvBP1[2])+(2.2876800081*yvBP1[3])
return yvBP1[4]
xvLP1 = [0 for _ in range(5)]
yvLP1 = [0 for _ in range(3)]
def lowPass1(sampleIn):
global xvLP1, yvLP1
xvLP1[0] = xvLP1[1]
xvLP1[1] = xvLP1[2]
xvLP1[2] = sampleIn / 2666.171709
yvLP1[0] = yvLP1[1]
yvLP1[1] = yvLP1[2]
yvLP1[2] = (xvLP1[0]+xvLP1[2])+2*xvLP1[1]+(-0.9459779362*yvLP1[0])+(1.9444776578*yvLP1[1])
return yvLP1[2]
xvBP2 = [0 for _ in range(5)]
yvBP2 = [0 for _ in range(5)]
def bandPassFreq2(sampleIn):
global xvBP2, yvBP2
xvBP2[0] = xvBP2[1]
xvBP2[1] = xvBP2[2]
xvBP2[2] = xvBP2[3]
xvBP2[3] = xvBP2[4]
xvBP2[4] = sampleIn / 684.7831430
yvBP2[0] = yvBP2[1]
yvBP2[1] = yvBP2[2]
yvBP2[2] = yvBP2[3]
yvBP2[3] = yvBP2[4]
yvBP2[4] = (xvBP2[0]+xvBP2[4])-2*xvBP2[2]+(-0.8948743446*yvBP2[0])+(1.9236412517*yvBP2[1])+(-2.9236524734*yvBP2[2])+(2.0335820202*yvBP2[3])
return yvBP2[4]
xvLP2 = [0 for _ in range(5)]
yvLP2 = [0 for _ in range(3)]
def lowPass2(sampleIn):
global xvLP2, yvLP2
xvLP2[0] = xvLP2[1]
xvLP2[1] = xvLP2[2]
xvLP2[2] = sampleIn / 2666.171709
yvLP2[0] = yvLP2[1]
yvLP2[1] = yvLP2[2]
yvLP2[2] = (xvLP2[0]+xvLP2[2])+2*xvLP2[1]+(-0.9459779362*yvLP2[0])+(1.9444776578*yvLP2[1])
return yvLP2[2]
xvBP3 = [0 for _ in range(5)]
yvBP3 = [0 for _ in range(5)]
def bandPassFreq3(sampleIn):
global xvBP3, yvBP3
xvBP3[0] = xvBP3[1]
xvBP3[1] = xvBP3[2]
xvBP3[2] = xvBP3[3]
xvBP3[3] = xvBP3[4]
xvBP3[4] = sampleIn / 684.7831487
yvBP3[0] = yvBP3[1]
yvBP3[1] = yvBP3[2]
yvBP3[2] = yvBP3[3]
yvBP3[3] = yvBP3[4]
yvBP3[4] = (xvBP3[0]+xvBP3[4])-2*xvBP3[2]+(-0.8948743446*yvBP3[0])+(1.4088950412*yvBP3[1])+(-2.4440289254*yvBP3[2])+(1.4894168138*yvBP3[3])
return yvBP3[4]
xvLP3 = [0 for _ in range(5)]
yvLP3 = [0 for _ in range(3)]
def lowPass3(sampleIn):
global xvLP3, yvLP3
xvLP3[0] = xvLP3[1]
xvLP3[1] = xvLP3[2]
xvLP3[2] = sampleIn / 2666.171709
yvLP3[0] = yvLP3[1]
yvLP3[1] = yvLP3[2]
yvLP3[2] = (xvLP3[0]+xvLP3[2])+2*xvLP3[1]+(-0.9459779362*yvLP3[0])+(1.9444776578*yvLP3[1])
return yvLP3[2]
xvBP4 = [0 for _ in range(5)]
yvBP4 = [0 for _ in range(5)]
def bandPassFreq4(sampleIn):
global xvBP4, yvBP4
xvBP4[0] = xvBP4[1]
xvBP4[1] = xvBP4[2]
xvBP4[2] = xvBP4[3]
xvBP4[3] = xvBP4[4];
xvBP4[4] = sampleIn / 684.7831502
yvBP4[0] = yvBP4[1]
yvBP4[1] = yvBP4[2]
yvBP4[2] = yvBP4[3]
yvBP4[3] = yvBP4[4]
yvBP4[4] = (xvBP4[0]+xvBP4[4])-2*xvBP4[2]+(-0.8948743446*yvBP4[0])+(0.2888565889*yvBP4[1])+(-1.9123621269*yvBP4[2])+(0.3053654444*yvBP4[3])
return yvBP4[4]
xvLP4 = [0 for _ in range(5)]
yvLP4 = [0 for _ in range(3)]
def lowPass4(sampleIn):
xvLP4[0] = xvLP4[1]
xvLP4[1] = xvLP4[2]
xvLP4[2] = sampleIn / 2666.171709
yvLP4[0] = yvLP4[1]
yvLP4[1] = yvLP4[2]
yvLP4[2] = (xvLP4[0]+xvLP4[2])+2*xvLP4[1]+(-0.9459779362*yvLP4[0])+(1.9444776578*yvLP4[1])
return yvLP4[2]
def demodulator(sample):
lines = [0 for _ in range(5)]
lines[0] = bandPassFreq0(sample)
lines[1] = bandPassFreq1(sample)
lines[2] = bandPassFreq2(sample)
lines[3] = bandPassFreq3(sample)
lines[4] = bandPassFreq4(sample)
lines[0] = lines[0] * lines[0]
lines[1] = lines[1] * lines[1]
lines[2] = lines[2] * lines[2]
lines[3] = lines[3] * lines[3]
lines[4] = lines[4] * lines[4]
lines[0] = lowPass0(lines[0])
lines[1] = lowPass1(lines[1])
lines[2] = lowPass2(lines[2])
lines[3] = lowPass3(lines[3])
lines[4] = lowPass4(lines[4])
maxIndex = 0
maxVal = lines[0]
for i in range(1, len(lines)):
if (lines[i] > maxVal):
maxVal = lines[i]
maxIndex = i
return maxIndex
def getVisBit():
val = 0
for i in range(0, oneBitSampleCount):
val = demodulator(getSample())
return val
def startVISReceiving():
print ("Header received, starting receiving VIS code: ")
for i in range(0, int(oneBitSampleCount/2)):
demodulator(getSample())
VIS = 0
parity = 0
parityError = False
for i in range(0, 9):
bitResult = getVisBit()
if (bitResult == 0):
bit = 1
else:
bit = 0
if (i == 7):
if (parity != bit):
parityError = True
elif (i == 8):
print ("stop bit")
else:
if (bitResult == 0):
VIS |= bit << (i)
parity ^= bit
print (bit)
print("VIS: " + str(VIS))
if (parityError):
print (" parity ERROR")
else:
print (" parity OK")
if (VIS == 44):
print (" VIS MARTIN OK")
else:
print (" VIS UNKNOWN ERROR")
for i in range(0, int(oneBitSampleCount/4)+5):
demodulator(getSample())
def waitForEdge(result):
result[0] = result[1]
while (True):
result[1] = demodulator(getSample())
if (result[0] != result[1]):
break
result[0] = result[1]
return result
def waitForStart():
global count
result = [0 for _ in range(2)]
edge41Count = 0
while (True):
result = waitForEdge(result)
if (edge41Count == 0):
if (result[0] == 4 and result[1] == 1):
edge41Count = 1
print ("Got edge: 41", " count: ", count)
continue
elif (edge41Count == 1):
if (result[0] == 1 and result[1] == 4):
edge41Count = 2
print ("Got edge: 14", " count: ", count)
continue
elif (edge41Count == 2):
if (result[0] == 4 and result[1] == 1):
print ("Got edge: 41 -> 14 -> 41", " count: ", count)
startVISReceiving()
edge41Count = 0
wf = wave.open('mmsstv.wav', 'rb')
params = wf.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
print (nchannels,sampwidth,framerate,nframes)
str_data = wf.readframes(nframes)
waveData=np.fromstring(str_data,dtype=np.short)
waitForStart()
#every frame is 2 bytes
#every element in str_data is 1 byte
现在python代码可以解header和vis,离解出图片已经不远了,我只要用同样方法从java里往python里搬代码就行。
import wave
import numpy as np
from math import sin, cos, pi, floor
from PIL import Image
img = Image.new( 'RGB', (320,256), "white")
displayedImage = img.load()
#-- Initial Variables
SRate = 8000
oneBitSampleCount = int(SRate*0.030)
#index = 0
count = 0
def getSample():
global count
#sampleDouble = ((str_data[index] & 0xFF) | (str_data[index+1] << 8) ) / 32768.0
#print ("sampleDouble: ", sampleDouble, " sample[0]: ", str_data[index]," sample[1]: ", str_data[index+1],"count: ",count)
#print ("waveData: ", waveData[count], " count: ", count)
sampleDouble = waveData[count]/ 32768.0
#print ("waveData: ", sampleDouble, " count: ", count)
#index = index + 2
count = count + 1
return bandPassSSTVSignal(sampleDouble)
xvBPV = [0 for _ in range(5)]
yvBPV = [0 for _ in range(5)]
def bandPassSSTVSignal(sampleIn):
global xvBPV, yvBPV
xvBPV[0] = xvBPV[1]
xvBPV[1] = xvBPV[2]
xvBPV[2] = xvBPV[3]
xvBPV[3] = xvBPV[4]
xvBPV[4] = sampleIn / 7.627348301
yvBPV[0] = yvBPV[1]
yvBPV[1] = yvBPV[2]
yvBPV[2] = yvBPV[3]
yvBPV[3] = yvBPV[4]
yvBPV[4] = (xvBPV[0]+xvBPV[4])-2*xvBPV[2]+(-0.2722149379*yvBPV[0])+(0.3385637917*yvBPV[1])+(-0.8864523063*yvBPV[2])+(0.7199258671*yvBPV[3])
return yvBPV[4]
xvBP0 = [0 for _ in range(5)]
yvBP0 = [0 for _ in range(5)]
def bandPassFreq0(sampleIn):
global xvBP0, yvBP0
xvBP0[0] = xvBP0[1]
xvBP0[1] = xvBP0[2]
xvBP0[2] = xvBP0[3]
xvBP0[3] = xvBP0[4]
xvBP0[4] = sampleIn / 684.7831231
yvBP0[0] = yvBP0[1]
yvBP0[1] = yvBP0[2]
yvBP0[2] = yvBP0[3]
yvBP0[3] = yvBP0[4]
yvBP0[4] = (xvBP0[0]+xvBP0[4])-2*xvBP0[2]+(-0.8948743446*yvBP0[0])+(2.3910210304*yvBP0[1])+(-3.4874837696*yvBP0[2])+(2.5276736881*yvBP0[3])
return yvBP0[4]
xvLP0 = [0 for _ in range(5)]
yvLP0 = [0 for _ in range(3)]
def lowPass0(sampleIn):
global xvLP0, yvLP0
xvLP0[0] = xvLP0[1]
xvLP0[1] = xvLP0[2];
xvLP0[2] = sampleIn / 2666.171709
yvLP0[0] = yvLP0[1]
yvLP0[1] = yvLP0[2]
yvLP0[2] = (xvLP0[0]+xvLP0[2])+2*xvLP0[1]+(-0.9459779362*yvLP0[0])+(1.9444776578*yvLP0[1])
return yvLP0[2]
xvBP1 = [0 for _ in range(5)]
yvBP1 = [0 for _ in range(5)]
def bandPassFreq1(sampleIn):
global xvBP1, yvBP1
xvBP1[0] = xvBP1[1]
xvBP1[1] = xvBP1[2]
xvBP1[2] = xvBP1[3]
xvBP1[3] = xvBP1[4]
xvBP1[4] = sampleIn / 684.7831360
yvBP1[0] = yvBP1[1]
yvBP1[1] = yvBP1[2]
yvBP1[2] = yvBP1[3]
yvBP1[3] = yvBP1[4]
yvBP1[4] = (xvBP1[0]+xvBP1[4])-2*xvBP1[2]+(-0.8948743446*yvBP1[0])+(2.1640020371*yvBP1[1])+(-3.1983590493*yvBP1[2])+(2.2876800081*yvBP1[3])
return yvBP1[4]
xvLP1 = [0 for _ in range(5)]
yvLP1 = [0 for _ in range(3)]
def lowPass1(sampleIn):
global xvLP1, yvLP1
xvLP1[0] = xvLP1[1]
xvLP1[1] = xvLP1[2]
xvLP1[2] = sampleIn / 2666.171709
yvLP1[0] = yvLP1[1]
yvLP1[1] = yvLP1[2]
yvLP1[2] = (xvLP1[0]+xvLP1[2])+2*xvLP1[1]+(-0.9459779362*yvLP1[0])+(1.9444776578*yvLP1[1])
return yvLP1[2]
xvBP2 = [0 for _ in range(5)]
yvBP2 = [0 for _ in range(5)]
def bandPassFreq2(sampleIn):
global xvBP2, yvBP2
xvBP2[0] = xvBP2[1]
xvBP2[1] = xvBP2[2]
xvBP2[2] = xvBP2[3]
xvBP2[3] = xvBP2[4]
xvBP2[4] = sampleIn / 684.7831430
yvBP2[0] = yvBP2[1]
yvBP2[1] = yvBP2[2]
yvBP2[2] = yvBP2[3]
yvBP2[3] = yvBP2[4]
yvBP2[4] = (xvBP2[0]+xvBP2[4])-2*xvBP2[2]+(-0.8948743446*yvBP2[0])+(1.9236412517*yvBP2[1])+(-2.9236524734*yvBP2[2])+(2.0335820202*yvBP2[3])
return yvBP2[4]
xvLP2 = [0 for _ in range(5)]
yvLP2 = [0 for _ in range(3)]
def lowPass2(sampleIn):
global xvLP2, yvLP2
xvLP2[0] = xvLP2[1]
xvLP2[1] = xvLP2[2]
xvLP2[2] = sampleIn / 2666.171709
yvLP2[0] = yvLP2[1]
yvLP2[1] = yvLP2[2]
yvLP2[2] = (xvLP2[0]+xvLP2[2])+2*xvLP2[1]+(-0.9459779362*yvLP2[0])+(1.9444776578*yvLP2[1])
return yvLP2[2]
xvBP3 = [0 for _ in range(5)]
yvBP3 = [0 for _ in range(5)]
def bandPassFreq3(sampleIn):
global xvBP3, yvBP3
xvBP3[0] = xvBP3[1]
xvBP3[1] = xvBP3[2]
xvBP3[2] = xvBP3[3]
xvBP3[3] = xvBP3[4]
xvBP3[4] = sampleIn / 684.7831487
yvBP3[0] = yvBP3[1]
yvBP3[1] = yvBP3[2]
yvBP3[2] = yvBP3[3]
yvBP3[3] = yvBP3[4]
yvBP3[4] = (xvBP3[0]+xvBP3[4])-2*xvBP3[2]+(-0.8948743446*yvBP3[0])+(1.4088950412*yvBP3[1])+(-2.4440289254*yvBP3[2])+(1.4894168138*yvBP3[3])
return yvBP3[4]
xvLP3 = [0 for _ in range(5)]
yvLP3 = [0 for _ in range(3)]
def lowPass3(sampleIn):
global xvLP3, yvLP3
xvLP3[0] = xvLP3[1]
xvLP3[1] = xvLP3[2]
xvLP3[2] = sampleIn / 2666.171709
yvLP3[0] = yvLP3[1]
yvLP3[1] = yvLP3[2]
yvLP3[2] = (xvLP3[0]+xvLP3[2])+2*xvLP3[1]+(-0.9459779362*yvLP3[0])+(1.9444776578*yvLP3[1])
return yvLP3[2]
xvBP4 = [0 for _ in range(5)]
yvBP4 = [0 for _ in range(5)]
def bandPassFreq4(sampleIn):
global xvBP4, yvBP4
xvBP4[0] = xvBP4[1]
xvBP4[1] = xvBP4[2]
xvBP4[2] = xvBP4[3]
xvBP4[3] = xvBP4[4];
xvBP4[4] = sampleIn / 684.7831502
yvBP4[0] = yvBP4[1]
yvBP4[1] = yvBP4[2]
yvBP4[2] = yvBP4[3]
yvBP4[3] = yvBP4[4]
yvBP4[4] = (xvBP4[0]+xvBP4[4])-2*xvBP4[2]+(-0.8948743446*yvBP4[0])+(0.2888565889*yvBP4[1])+(-1.9123621269*yvBP4[2])+(0.3053654444*yvBP4[3])
return yvBP4[4]
xvLP4 = [0 for _ in range(5)]
yvLP4 = [0 for _ in range(3)]
def lowPass4(sampleIn):
xvLP4[0] = xvLP4[1]
xvLP4[1] = xvLP4[2]
xvLP4[2] = sampleIn / 2666.171709
yvLP4[0] = yvLP4[1]
yvLP4[1] = yvLP4[2]
yvLP4[2] = (xvLP4[0]+xvLP4[2])+2*xvLP4[1]+(-0.9459779362*yvLP4[0])+(1.9444776578*yvLP4[1])
return yvLP4[2]
def demodulator(sample):
lines = [0 for _ in range(5)]
lines[0] = bandPassFreq0(sample)
lines[1] = bandPassFreq1(sample)
lines[2] = bandPassFreq2(sample)
lines[3] = bandPassFreq3(sample)
lines[4] = bandPassFreq4(sample)
lines[0] = lines[0] * lines[0]
lines[1] = lines[1] * lines[1]
lines[2] = lines[2] * lines[2]
lines[3] = lines[3] * lines[3]
lines[4] = lines[4] * lines[4]
lines[0] = lowPass0(lines[0])
lines[1] = lowPass1(lines[1])
lines[2] = lowPass2(lines[2])
lines[3] = lowPass3(lines[3])
lines[4] = lowPass4(lines[4])
maxIndex = 0
maxVal = lines[0]
for i in range(1, len(lines)):
if (lines[i] > maxVal):
maxVal = lines[i]
maxIndex = i
return maxIndex
def getVisBit():
val = 0
for i in range(0, oneBitSampleCount):
val = demodulator(getSample())
return val
def startVISReceiving():
print ("Header received, starting receiving VIS code: ")
for i in range(0, int(oneBitSampleCount/2)):
demodulator(getSample())
VIS = 0
parity = 0
parityError = False
for i in range(0, 9):
bitResult = getVisBit()
if (bitResult == 0):
bit = 1
else:
bit = 0
if (i == 7):
if (parity != bit):
parityError = True
elif (i == 8):
print ("stop bit")
else:
if (bitResult == 0):
VIS |= bit << (i)
parity ^= bit
print (bit)
print("VIS: " + str(VIS))
if (parityError):
print (" parity ERROR")
else:
print (" parity OK")
if (VIS == 44):
print (" VIS MARTIN OK")
else:
print (" VIS UNKNOWN ERROR")
for i in range(0, int(oneBitSampleCount/4)+5):
demodulator(getSample())
def waitForEdge(result):
result[0] = result[1]
while (True):
result[1] = demodulator(getSample())
if (result[0] != result[1]):
break
result[0] = result[1]
return result
def waitForStart():
global count
result = [0 for _ in range(2)]
edge41Count = 0
while (True):
result = waitForEdge(result)
if (edge41Count == 0):
if (result[0] == 4 and result[1] == 1):
edge41Count = 1
print ("Got edge: 41", " count: ", count)
continue
elif (edge41Count == 1):
if (result[0] == 1 and result[1] == 4):
edge41Count = 2
print ("Got edge: 14", " count: ", count)
continue
elif (edge41Count == 2):
if (result[0] == 4 and result[1] == 1):
print ("Got edge: 41 -> 14 -> 41", " count: ", count)
startVISReceiving()
receiveImage()
break
edge41Count = 0
FIRLPCoeffs = [ +0.0001082461, +0.0034041195, +0.0063570207, +0.0078081648,
+0.0060550614, -0.0002142384, -0.0104500335, -0.0211855480,
-0.0264527776, -0.0201269304, +0.0004419626, +0.0312014771,
+0.0606261038, +0.0727491887, +0.0537028370, -0.0004362161,
-0.0779387981, -0.1511168919, -0.1829049634, -0.1390189257,
-0.0017097774, +0.2201896764, +0.4894395006, +0.7485289338,
+0.9357596142, +1.0040320616, +0.9357596142, +0.7485289338,
+0.4894395006, +0.2201896764, -0.0017097774, -0.1390189257,
-0.1829049634, -0.1511168919, -0.0779387981, -0.0004362161,
+0.0537028370, +0.0727491887, +0.0606261038, +0.0312014771,
+0.0004419626, -0.0201269304, -0.0264527776, -0.0211855480,
-0.0104500335, -0.0002142384, +0.0060550614, +0.0078081648,
+0.0063570207, +0.0034041195, +0.0001082461]
xvFIRLP1 = [0 for _ in range(len(FIRLPCoeffs))]
def FIRLowPass1(sampleIn):
for i in range(0, len(xvFIRLP1)-1):
xvFIRLP1[i] = xvFIRLP1[i+1]
xvFIRLP1[len(xvFIRLP1)-1] = sampleIn / 5.013665674
sum = 0
for i in range(0, len(xvFIRLP1)):
sum += (FIRLPCoeffs[i] * xvFIRLP1[i])
return sum
xvFIRLP2 = [0 for _ in range(len(FIRLPCoeffs))]
def FIRLowPass2(sampleIn):
for i in range(0, len(xvFIRLP2)-1):
xvFIRLP2[i] = xvFIRLP2[i+1]
xvFIRLP2[len(xvFIRLP2)-1] = sampleIn / 5.013665674
sum = 0
for i in range(0, len(xvFIRLP2)):
sum += (FIRLPCoeffs[i] * xvFIRLP2[i])
return sum
xvMA1 = [0 for _ in range(9)]
yvMA1prev = 0
def noiseReductionFilter1(sampleIn):
global yvMA1prev
for i in range(0, len(xvMA1)-1):
xvMA1[i] = xvMA1[i+1]
xvMA1[len(xvMA1)-1] = sampleIn
yvMA1prev = yvMA1prev + xvMA1[len(xvMA1)-1] - xvMA1[0]
return yvMA1prev
xvMA2 = [0 for _ in range(9)]
yvMA2prev = 0
def noiseReductionFilter2(sampleIn):
global yvMA2prev
for i in range(0, len(xvMA2)-1):
xvMA2[i] = xvMA2[i+1]
xvMA2[len(xvMA2)-1] = sampleIn
yvMA2prev = yvMA2prev + xvMA2[len(xvMA2)-1] - xvMA2[0]
return yvMA2prev
oscPhase = 0
realPartPrev = 0
imaginaryPartPrev = 0
def fmDemodulateLuminance(sample):
global oscPhase, realPartPrev, imaginaryPartPrev
oscPhase = oscPhase + (2 * pi * 2000) / SRate
realPart = cos(oscPhase) * sample
imaginaryPart = sin(oscPhase) * sample
if (oscPhase >= 2 * pi):
oscPhase = oscPhase - 2 * pi
realPart = FIRLowPass1(realPart)
imaginaryPart = FIRLowPass2(imaginaryPart)
realPart = noiseReductionFilter1(realPart)
imaginaryPart = noiseReductionFilter2(imaginaryPart)
demod_result = (imaginaryPart*realPartPrev-realPart*imaginaryPartPrev)/(realPart*realPart+imaginaryPart*imaginaryPart)
realPartPrev = realPart
imaginaryPartPrev = imaginaryPart
demod_result = demod_result + 0.2335
luminance = int(round((demod_result/0.617)*255, 0))
luminance = 255-luminance
if (luminance > 255):
luminance = 255
if (luminance < 0):
luminance = 0
return luminance
def receiveImage():
pixelLengthInS = 0.0004576
syncLengthInS = 0.004862
porchLengthInS = 0.000572
separatorLengthInS = 0.000572
channelLengthInS = pixelLengthInS*320
channelGStartInS = syncLengthInS + porchLengthInS
channelBStartInS = channelGStartInS + channelLengthInS + separatorLengthInS
channelRStartInS = channelBStartInS + channelLengthInS + separatorLengthInS
lineLengthInS = syncLengthInS + porchLengthInS + channelLengthInS + separatorLengthInS + channelLengthInS + separatorLengthInS + channelLengthInS + separatorLengthInS
imageLengthInSamples = (lineLengthInS*256)*SRate
t = 0
linet = 0
nextSyncTime = 0
sample = 0
for s in range(0, int(imageLengthInSamples)):
t = s/SRate
linet = t % lineLengthInS
sample = getSample()
demodulator(sample)
lum = fmDemodulateLuminance(sample)
if (t >= nextSyncTime):
nextSyncTime = nextSyncTime + lineLengthInS
#img.show()
if ( (linet >= channelGStartInS and linet < channelGStartInS + channelLengthInS) or
(linet >= channelBStartInS and linet < channelBStartInS + channelLengthInS) or
(linet >= channelRStartInS and linet < channelRStartInS + channelLengthInS) ):
y = floor(t/lineLengthInS)
if (linet >= channelGStartInS and linet < channelGStartInS + channelLengthInS):
x = floor(((linet-channelGStartInS)/channelLengthInS)*320)
displayedImage[x,y] = (0, lum, 0)
if (linet >= channelBStartInS and linet < channelBStartInS + channelLengthInS):
x = floor(((linet-channelBStartInS)/channelLengthInS)*320)
displayedImage[x,y] = (0, displayedImage[x,y][1], lum)
if (linet >= channelRStartInS and linet < channelRStartInS + channelLengthInS):
x = floor(((linet-channelRStartInS)/channelLengthInS)*320)
displayedImage[x,y] = (lum, displayedImage[x,y][1], displayedImage[x,y][2])
img.show()
print ("Finished rx.")
wf = wave.open('mmsstv.wav', 'rb')
params = wf.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
print (nchannels,sampwidth,framerate,nframes)
str_data = wf.readframes(nframes)
waveData=np.fromstring(str_data,dtype=np.short)
waitForStart()
img.show()
上面这段代码就是我用python实现的martin m1的解调程序。
如果你想试试可以用单声道 ,16位,8kHz采样率的wav格式来试试,只支持martin m1格式。
接下去的工作就是要用c++改写了。
decode.cpp
#include
#include
#include
#include
#include
#include
#include
#include "wav.h"
struct wav_head {
uint32_t ChunkID;
uint32_t ChunkSize;
uint32_t Format;
uint32_t Subchunk1ID;
uint32_t Subchunk1Size;
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlign;
uint16_t BitsPerSample;
uint32_t Subchunk2ID;
uint32_t Subchunk2Size;
};
struct wav {
struct pcm base;
void *p;
short *b;
size_t size;
int index;
int frames;
int r;
int c;
};
int mmap_file_ro(void **p, char *name, size_t *size)
{
*size = 0;
int fd = open(name, O_RDONLY);
if (fd == -1) {
perror("open");
return 0;
}
struct stat sb;
if (fstat(fd, &sb) == -1) {
perror("fstat");
//close(fd);
return 0;
}
if (!S_ISREG(sb.st_mode)) {
fprintf(stderr, "%s not a file\n", name);
//close(fd);
return 0;
}
*p = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (*p == MAP_FAILED) {
perror("mmap");
//close(fd);
return 0;
}
/*if (close(fd) == -1) {
perror ("close");
return 0;
}*/
*size = sb.st_size;
return 1;
}
void close_wav(struct pcm *pcm)
{
struct wav *wav = (struct wav *)(pcm->data);
free(wav);
}
void info_wav(struct pcm *pcm)
{
struct wav *wav = (struct wav *)(pcm->data);
fprintf(stderr, "%d channel(s), %d rate, %.2f seconds\n", wav->c, wav->r, (float)wav->frames / (float)wav->r);
}
int read_wav(struct pcm *pcm, short *buff, int frames)
{
struct wav *wav = (struct wav *)(pcm->data);
if ((wav->index + frames) > wav->frames)
return 0;
memcpy(buff, wav->b + wav->index * wav->c, sizeof(short) * frames * wav->c);
wav->index += frames;
return 1;
}
int open_wav_read(struct pcm **p, char *name)
{
struct wav *wav = (struct wav *)malloc(sizeof(struct wav));
wav->base.close = close_wav;
wav->base.info = info_wav;
wav->base.rw = read_wav;
wav->base.data = (void *)wav;
if (!mmap_file_ro(&wav->p, name, &wav->size)) {
fprintf(stderr, "couldnt open wav file %s!\n", name);
free(wav);
return 0;
}
struct wav_head *head = (struct wav_head *)wav->p;
wav->b = (short *)(wav->p + sizeof(struct wav_head));
if (head->ChunkID != 0x46464952 || head->Format != 0x45564157 ||
head->Subchunk1ID != 0x20746d66 || head->Subchunk1Size != 16 ||
head->AudioFormat != 1 || head->Subchunk2ID != 0x61746164) {
fprintf(stderr, "unsupported WAV file!\n");
free(wav);
return 0;
}
if (head->BitsPerSample != 16) {
fprintf(stderr, "only 16bit WAV supported!\n");
free(wav);
return 0;
}
wav->index = 0;
wav->frames = head->Subchunk2Size / (sizeof(short) * head->NumChannels);
wav->r = head->SampleRate;
wav->c = head->NumChannels;
*p = &(wav->base);
return 1;
}
wav.c
#include
#include
#include
#include
#include
#include
#include
#include "wav.h"
struct wav_head {
uint32_t ChunkID;
uint32_t ChunkSize;
uint32_t Format;
uint32_t Subchunk1ID;
uint32_t Subchunk1Size;
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlign;
uint16_t BitsPerSample;
uint32_t Subchunk2ID;
uint32_t Subchunk2Size;
};
struct wav {
struct pcm base;
void *p;
short *b;
size_t size;
int index;
int frames;
int r;
int c;
};
int mmap_file_ro(void **p, char *name, size_t *size)
{
*size = 0;
int fd = open(name, O_RDONLY);
if (fd == -1) {
perror("open");
return 0;
}
struct stat sb;
if (fstat(fd, &sb) == -1) {
perror("fstat");
//close(fd);
return 0;
}
if (!S_ISREG(sb.st_mode)) {
fprintf(stderr, "%s not a file\n", name);
//close(fd);
return 0;
}
*p = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (*p == MAP_FAILED) {
perror("mmap");
//close(fd);
return 0;
}
/*if (close(fd) == -1) {
perror ("close");
return 0;
}*/
*size = sb.st_size;
return 1;
}
void close_wav(struct pcm *pcm)
{
struct wav *wav = (struct wav *)(pcm->data);
free(wav);
}
void info_wav(struct pcm *pcm)
{
struct wav *wav = (struct wav *)(pcm->data);
fprintf(stderr, "%d channel(s), %d rate, %.2f seconds\n", wav->c, wav->r, (float)wav->frames / (float)wav->r);
}
int read_wav(struct pcm *pcm, short *buff, int frames)
{
struct wav *wav = (struct wav *)(pcm->data);
if ((wav->index + frames) > wav->frames)
return 0;
memcpy(buff, wav->b + wav->index * wav->c, sizeof(short) * frames * wav->c);
wav->index += frames;
return 1;
}
int open_wav_read(struct pcm **p, char *name)
{
struct wav *wav = (struct wav *)malloc(sizeof(struct wav));
wav->base.close = close_wav;
wav->base.info = info_wav;
wav->base.rw = read_wav;
wav->base.data = (void *)wav;
if (!mmap_file_ro(&wav->p, name, &wav->size)) {
fprintf(stderr, "couldnt open wav file %s!\n", name);
free(wav);
return 0;
}
struct wav_head *head = (struct wav_head *)wav->p;
wav->b = (short *)(wav->p + sizeof(struct wav_head));
if (head->ChunkID != 0x46464952 || head->Format != 0x45564157 ||
head->Subchunk1ID != 0x20746d66 || head->Subchunk1Size != 16 ||
head->AudioFormat != 1 || head->Subchunk2ID != 0x61746164) {
fprintf(stderr, "unsupported WAV file!\n");
free(wav);
return 0;
}
if (head->BitsPerSample != 16) {
fprintf(stderr, "only 16bit WAV supported!\n");
free(wav);
return 0;
}
wav->index = 0;
wav->frames = head->Subchunk2Size / (sizeof(short) * head->NumChannels);
wav->r = head->SampleRate;
wav->c = head->NumChannels;
*p = &(wav->base);
return 1;
}
wav.h
struct pcm {
void (*close)(struct pcm *);
void (*info)(struct pcm *);
int (*rate)(struct pcm *);
int (*channels)(struct pcm *);
int (*rw)(struct pcm *, short *, int);
void *data;
};
int read_wav(struct pcm *pcm, short *buff, int frames);
void info_wav(struct pcm *pcm);
void close_wav(struct pcm *pcm);
int open_wav_read(struct pcm **, char *);
g++ decode.cpp wav.cpp -o decode -lm `pkg-config --cflags --libs opencv`
./decode
我昨天晚上又改了下代码,实现了如下效果:
https://www.bilibili.com/video/BV1vV411k7D3
现在已经可以用hackrf实时解调空中的无线电波了,过程是:
1.hackrf_fm从无线电波解到声波
2.decode从声波解到图像
我对decode的程序 修改主要 是把wav读取部分替换为了alsa输入 ,另外 vis判断不通过就不去解图像,通过了才收图像。
decode.cpp
#include
#include
#include
#include
#include
#include
#include
#include "alsa.h"
#include
#include
#include
using namespace cv;
struct pcm *pcm;
Mat frame;
int counter = 0;
int SAMPLERATE = 8000;
double oneBitSampleCount;
double round(double r)
{
return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}
double xvBPV[5];
double yvBPV[5];
double bandPassSSTVSignal(double sampleIn)
{
xvBPV[0] = xvBPV[1]; xvBPV[1] = xvBPV[2];
xvBPV[2] = xvBPV[3]; xvBPV[3] = xvBPV[4];
xvBPV[4] = sampleIn / 7.627348301;
yvBPV[0] = yvBPV[1]; yvBPV[1] = yvBPV[2];
yvBPV[2] = yvBPV[3]; yvBPV[3] = yvBPV[4];
yvBPV[4] = (xvBPV[0]+xvBPV[4])-2*xvBPV[2]+(-0.2722149379*yvBPV[0])+(0.3385637917*yvBPV[1])+(-0.8864523063*yvBPV[2])+(0.7199258671*yvBPV[3]);
return yvBPV[4];
}
double xvBP0[5];
double yvBP0[5];
double bandPassFreq0(double sampleIn)
{
xvBP0[0] = xvBP0[1];
xvBP0[1] = xvBP0[2];
xvBP0[2] = xvBP0[3];
xvBP0[3] = xvBP0[4];
xvBP0[4] = sampleIn / 684.7831231;
yvBP0[0] = yvBP0[1];
yvBP0[1] = yvBP0[2];
yvBP0[2] = yvBP0[3];
yvBP0[3] = yvBP0[4];
yvBP0[4] = (xvBP0[0]+xvBP0[4])-2*xvBP0[2]+(-0.8948743446*yvBP0[0])+(2.3910210304*yvBP0[1])+(-3.4874837696*yvBP0[2])+(2.5276736881*yvBP0[3]);
return yvBP0[4];
}
double xvLP0[5];
double yvLP0[3];
double lowPass0(double sampleIn)
{
xvLP0[0] = xvLP0[1];
xvLP0[1] = xvLP0[2];
xvLP0[2] = sampleIn / 2.666171709e+03;
yvLP0[0] = yvLP0[1];
yvLP0[1] = yvLP0[2];
yvLP0[2] = (xvLP0[0]+xvLP0[2])+2*xvLP0[1]+(-0.9459779362*yvLP0[0])+(1.9444776578*yvLP0[1]);
return yvLP0[2];
}
double xvBP1[5];
double yvBP1[5];
double bandPassFreq1(double sampleIn)
{
xvBP1[0] = xvBP1[1];
xvBP1[1] = xvBP1[2];
xvBP1[2] = xvBP1[3];
xvBP1[3] = xvBP1[4];
xvBP1[4] = sampleIn / 684.7831360;
yvBP1[0] = yvBP1[1];
yvBP1[1] = yvBP1[2];
yvBP1[2] = yvBP1[3];
yvBP1[3] = yvBP1[4];
yvBP1[4] = (xvBP1[0]+xvBP1[4])-2*xvBP1[2]+(-0.8948743446*yvBP1[0])+(2.1640020371*yvBP1[1])+(-3.1983590493*yvBP1[2])+(2.2876800081*yvBP1[3]);
return yvBP1[4];
}
double xvLP1[5];
double yvLP1[3];
double lowPass1(double sampleIn)
{
xvLP1[0] = xvLP1[1];
xvLP1[1] = xvLP1[2];
xvLP1[2] = sampleIn / 2666.171709;
yvLP1[0] = yvLP1[1];
yvLP1[1] = yvLP1[2];
yvLP1[2] = (xvLP1[0]+xvLP1[2])+2*xvLP1[1]+(-0.9459779362*yvLP1[0])+(1.9444776578*yvLP1[1]);
return yvLP1[2];
}
double xvBP2[5];
double yvBP2[5];
double bandPassFreq2(double sampleIn)
{
xvBP2[0] = xvBP2[1];
xvBP2[1] = xvBP2[2];
xvBP2[2] = xvBP2[3];
xvBP2[3] = xvBP2[4];
xvBP2[4] = sampleIn / 684.7831430e+02;
yvBP2[0] = yvBP2[1];
yvBP2[1] = yvBP2[2];
yvBP2[2] = yvBP2[3];
yvBP2[3] = yvBP2[4];
yvBP2[4] = (xvBP2[0]+xvBP2[4])-2*xvBP2[2]+(-0.8948743446*yvBP2[0])+(1.9236412517*yvBP2[1])+(-2.9236524734*yvBP2[2])+(2.0335820202*yvBP2[3]);
return yvBP2[4];
}
double xvLP2[5];
double yvLP2[3];
double lowPass2(double sampleIn)
{
xvLP2[0] = xvLP2[1];
xvLP2[1] = xvLP2[2];
xvLP2[2] = sampleIn / 2666.171709;
yvLP2[0] = yvLP2[1]; yvLP2[1] = yvLP2[2];
yvLP2[2] = (xvLP2[0]+xvLP2[2])+2*xvLP2[1]+(-0.9459779362*yvLP2[0])+(1.9444776578*yvLP2[1]);
return yvLP2[2];
}
double xvBP3[5];
double yvBP3[5];
double bandPassFreq3(double sampleIn)
{
xvBP3[0] = xvBP3[1];
xvBP3[1] = xvBP3[2];
xvBP3[2] = xvBP3[3];
xvBP3[3] = xvBP3[4];
xvBP3[4] = sampleIn / 684.7831487;
yvBP3[0] = yvBP3[1];
yvBP3[1] = yvBP3[2];
yvBP3[2] = yvBP3[3];
yvBP3[3] = yvBP3[4];
yvBP3[4] = (xvBP3[0]+xvBP3[4])-2*xvBP3[2]+(-0.8948743446*yvBP3[0])+(1.4088950412*yvBP3[1])+(-2.4440289254*yvBP3[2])+(1.4894168138*yvBP3[3]);
return yvBP3[4];
}
double xvLP3[5];
double yvLP3[3];
double lowPass3(double sampleIn)
{
xvLP3[0] = xvLP3[1];
xvLP3[1] = xvLP3[2];
xvLP3[2] = sampleIn / 2666.171709;
yvLP3[0] = yvLP3[1];
yvLP3[1] = yvLP3[2];
yvLP3[2] = (xvLP3[0]+xvLP3[2])+2*xvLP3[1]+(-0.9459779362*yvLP3[0])+(1.9444776578*yvLP3[1]);
return yvLP3[2];
}
double xvBP4[5];
double yvBP4[5];
double bandPassFreq4(double sampleIn)
{
xvBP4[0] = xvBP4[1];
xvBP4[1] = xvBP4[2];
xvBP4[2] = xvBP4[3];
xvBP4[3] = xvBP4[4];
xvBP4[4] = sampleIn / 684.7831502;
yvBP4[0] = yvBP4[1];
yvBP4[1] = yvBP4[2];
yvBP4[2] = yvBP4[3];
yvBP4[3] = yvBP4[4];
yvBP4[4] = (xvBP4[0]+xvBP4[4])-2*xvBP4[2]+(-0.8948743446*yvBP4[0])+(0.2888565889*yvBP4[1])+(-1.9123621269*yvBP4[2])+(0.3053654444*yvBP4[3]);
return yvBP4[4];
}
double xvLP4[5];
double yvLP4[3];
double lowPass4(double sampleIn)
{
xvLP4[0] = xvLP4[1];
xvLP4[1] = xvLP4[2];
xvLP4[2] = sampleIn / 2666.171709;
yvLP4[0] = yvLP4[1];
yvLP4[1] = yvLP4[2];
yvLP4[2] = (xvLP4[0]+xvLP4[2])+2*xvLP4[1]+(-0.9459779362*yvLP4[0])+(1.9444776578*yvLP4[1]);
return yvLP4[2];
}
double getSample()
{
double sampleDouble = 0;
static short *pcm_buff;
pcm_buff = (short *)malloc(sizeof(short) * 1);
if (!read_alsa(pcm, pcm_buff, 1))
{
free(pcm_buff);
return 0;
}
sampleDouble = ((double)pcm_buff[0]) / 32768.0;
//sampleDouble = ((double)result_demod[counter]) / 32768.0;
counter = counter + 1;
/*if (counter == result_demod_len)
{
counter = 0;
}*/
//fprintf(stderr, "output data: %lf counter: %d\n", sampleDouble, counter);
return bandPassSSTVSignal(sampleDouble);
}
int demodulator(double sample)
{
double lines[5];
lines[0] = bandPassFreq0(sample);
lines[1] = bandPassFreq1(sample);
lines[2] = bandPassFreq2(sample);
lines[3] = bandPassFreq3(sample);
lines[4] = bandPassFreq4(sample);
// calculating the RMS of the lines (squaring them)
lines[0] *= lines[0];
lines[1] *= lines[1];
lines[2] *= lines[2];
lines[3] *= lines[3];
lines[4] *= lines[4];
// lowpass filtering the lines
lines[0] = lowPass0(lines[0]);
lines[1] = lowPass1(lines[1]);
lines[2] = lowPass2(lines[2]);
lines[3] = lowPass3(lines[3]);
lines[4] = lowPass4(lines[4]);
// deciding which line is the highest and returning it's index
int maxIndex = 0;
double maxVal = lines[0];
for (int i = 1; i < 5; i++)
{
if (lines[i] > maxVal)
{
maxVal = lines[i];
maxIndex = i;
}
}
return maxIndex;
}
int getVisBit()
{
int val = 0;
for (int i = 0; i < oneBitSampleCount; i++)
{
val = demodulator(getSample());
}
return val;
}
int startVISReceiving()
{
int bitResult;
fprintf(stderr, "Header received, starting receiving VIS code: \n");
oneBitSampleCount = SAMPLERATE*0.030;
// waiting half bit time (15ms)
for (int i = 0; i < oneBitSampleCount/2; i++)
{
demodulator(getSample());
}
int VIS = 0;
int parity = 0;
int parityError = 0;
for (int i = 0; i < 9; i++)
{
bitResult = getVisBit();
int bit = bitResult == 0 ? 1 : 0;
switch (i)
{
case 7:
if (parity != bit)
parityError = 1;
break;
case 8:
break; // stop bit
default:
if (bitResult == 0)
VIS |= bit << (i);
parity ^= bit;
fprintf(stderr, "%d\n", bit);
}
}
fprintf(stderr, "VIS: %d", VIS);
if (parityError)
fprintf(stderr, " parity ERROR");
else
fprintf(stderr, " parity OK");
if (VIS == 44) // Martin M1 VIS code
fprintf(stderr, " VIS MARTIN OK\n");
else
fprintf(stderr, " VIS UNKNOWN ERRO\n");
// waiting to reach the end of the stop bit
for (int i = 0; i < oneBitSampleCount/4+5; i++)
{
demodulator(getSample());
}
if (!parityError && VIS == 44)
{
fprintf(stderr, "start decoding\n");
return 1;
}
else
return 0;
}
void waitForEdge(int* result)
{
result[0] = result[1];
while (1)
{
result[1] = demodulator(getSample());
if (result[0] != result[1])
break;
result[0] = result[1];
}
return;
}
double FIRLPCoeffs[51] = { +0.0001082461, +0.0034041195, +0.0063570207, +0.0078081648,
+0.0060550614, -0.0002142384, -0.0104500335, -0.0211855480,
-0.0264527776, -0.0201269304, +0.0004419626, +0.0312014771,
+0.0606261038, +0.0727491887, +0.0537028370, -0.0004362161,
-0.0779387981, -0.1511168919, -0.1829049634, -0.1390189257,
-0.0017097774, +0.2201896764, +0.4894395006, +0.7485289338,
+0.9357596142, +1.0040320616, +0.9357596142, +0.7485289338,
+0.4894395006, +0.2201896764, -0.0017097774, -0.1390189257,
-0.1829049634, -0.1511168919, -0.0779387981, -0.0004362161,
+0.0537028370, +0.0727491887, +0.0606261038, +0.0312014771,
+0.0004419626, -0.0201269304, -0.0264527776, -0.0211855480,
-0.0104500335, -0.0002142384, +0.0060550614, +0.0078081648,
+0.0063570207, +0.0034041195, +0.0001082461,
};
double xvFIRLP1[51];
double FIRLowPass1(double sampleIn)
{
for (int i = 0; i < 50; i++)
xvFIRLP1[i] = xvFIRLP1[i+1];
xvFIRLP1[50] = sampleIn / 5.013665674;
double sum = 0;
for (int i = 0; i <= 50; i++)
sum += (FIRLPCoeffs[i] * xvFIRLP1[i]);
return sum;
}
double xvFIRLP2[51];
double FIRLowPass2(double sampleIn)
{
for (int i = 0; i < 50; i++)
xvFIRLP2[i] = xvFIRLP2[i+1];
xvFIRLP2[50] = sampleIn / 5.013665674;
double sum = 0;
for (int i = 0; i <= 50; i++)
sum += (FIRLPCoeffs[i] * xvFIRLP2[i]);
return sum;
}
// moving average
double xvMA1[9];
double yvMA1prev = 0;
double noiseReductionFilter1(double sampleIn)
{
for (int i = 0; i < 8; i++)
xvMA1[i] = xvMA1[i+1];
xvMA1[8] = sampleIn;
yvMA1prev = yvMA1prev+xvMA1[8]-xvMA1[0];
return yvMA1prev;
}
double xvMA2[9];
double yvMA2prev = 0;
double noiseReductionFilter2(double sampleIn)
{
for (int i = 0; i < 8; i++)
xvMA2[i] = xvMA2[i+1];
xvMA2[8] = sampleIn;
yvMA2prev = yvMA2prev+xvMA2[8]-xvMA2[0];
return yvMA2prev;
}
double oscPhase = 0;
double realPartPrev = 0;
double imaginaryPartPrev = 0;
int fmDemodulateLuminance(double sample)
{
oscPhase += (2 * M_PI * 2000) / SAMPLERATE;
double realPart = cos(oscPhase) * sample;
double imaginaryPart = sin(oscPhase) * sample;
if (oscPhase >= 2 * M_PI)
oscPhase -= 2 * M_PI;
realPart = FIRLowPass1(realPart);
imaginaryPart = FIRLowPass2(imaginaryPart);
realPart = noiseReductionFilter1(realPart);
imaginaryPart = noiseReductionFilter2(imaginaryPart);
sample = (imaginaryPart*realPartPrev-realPart*imaginaryPartPrev)/(realPart*realPart+imaginaryPart*imaginaryPart);
realPartPrev = realPart;
imaginaryPartPrev = imaginaryPart;
sample += 0.2335; // bring the value above 0
int luminance = (int)round((sample/0.617)*255);
luminance = 255-luminance;
if (luminance > 255)
luminance = 255;
if (luminance < 0)
luminance = 0;
return luminance;
}
void receiveImage()
{
double pixelLengthInS = 0.0004576;
double syncLengthInS = 0.004862;
double porchLengthInS = 0.000572;
double separatorLengthInS = 0.000572;
double channelLengthInS = pixelLengthInS*320;
double channelGStartInS = syncLengthInS + porchLengthInS;
double channelBStartInS = channelGStartInS + channelLengthInS + separatorLengthInS;
double channelRStartInS = channelBStartInS + channelLengthInS + separatorLengthInS;
double lineLengthInS = syncLengthInS + porchLengthInS + channelLengthInS + separatorLengthInS + channelLengthInS + separatorLengthInS + channelLengthInS + separatorLengthInS;
double imageLengthInSamples = (lineLengthInS*256)*SAMPLERATE;
double t, linet;
double nextSyncTime = 0;
double sample;
for (int s = 0; s < imageLengthInSamples; s++)
{
t = ((double)s)/((double)SAMPLERATE);
//linet = t % lineLengthInS;
int temp_lineLengthInS = (int)(lineLengthInS * 10000000);
int temp_t = (int)(t * 10000000);
linet = ((double)(temp_t % temp_lineLengthInS))/10000000;
sample = getSample();
demodulator(sample);
int lum = fmDemodulateLuminance(sample);
//fprintf(stderr, "lum: %d counter: %d\n", lum, counter);
if (t >= nextSyncTime)
{
nextSyncTime += lineLengthInS;
//fprintf(stderr, "t: %f next:%f \n", t, nextSyncTime);
imshow("frame", frame);
if (waitKey(5) == 'q')
{
break;
}
}
if ((linet >= channelGStartInS && linet < channelGStartInS + channelLengthInS) ||
(linet >= channelBStartInS && linet < channelBStartInS + channelLengthInS) ||
(linet >= channelRStartInS && linet < channelRStartInS + channelLengthInS) )
{
int y = (int)floor(t/lineLengthInS);
if (linet >= channelGStartInS && linet < channelGStartInS + channelLengthInS)
{
int x = (int)floor(((linet-channelGStartInS)/channelLengthInS)*320);
//fprintf(stderr, "lum: %d \n", lum);
frame.at(y, x)[1] = lum;
}
if (linet >= channelBStartInS && linet < channelBStartInS + channelLengthInS)
{
int x = (int)floor(((linet-channelBStartInS)/channelLengthInS)*320);
frame.at(y, x)[0] = lum;
}
if (linet >= channelRStartInS && linet < channelRStartInS + channelLengthInS)
{
int x = (int)floor(((linet-channelRStartInS)/channelLengthInS)*320);
frame.at(y, x)[2] = lum;
}
}
}
//imshow("frame", frame);
fprintf(stderr, "Finished rx.\n");
}
void waitForStart()
{
int result[2] = {0,0};
static int r[10];
int edge41Count = 0;
while (1)
{
waitForEdge(result);
// this part starts VIS receiving if we receive 41 -> 14 -> 41 transitions (bits 4141) in sequence
switch (edge41Count)
{
case 0:
if (result[0] == 4 && result[1] == 1)
{
edge41Count = 1;
fprintf(stderr, "Got edge: 41\n");
continue;
}
break;
case 1:
if (result[0] == 1 && result[1] == 4)
{ // got 41 -> 14
edge41Count = 2;
fprintf(stderr, "Got edge: 14\n");
continue;
}
break;
case 2:
if (result[0] == 4 && result[1] == 1)
{ // got 41 -> 14 -> 41
fprintf(stderr, "Got edge: 41 -> 14 -> 41\n");
int ok = startVISReceiving();
if (ok == 1)
{
receiveImage();
}
}
break;
}
edge41Count = 0;
/*if (waitKey(5) == 'q')
{
break;
}*/
}
}
int main(int argc, char **argv)
{
char *pcm_name;
pcm_name = "default";
frame = Mat::zeros(256, 320, CV_8UC3);
if (!open_alsa_read(&pcm, pcm_name))
return 1;
while (1)
{
waitForStart();
}
return 0;
}
alsa.cpp
#include
#include
#include
#include "alsa.h"
struct alsa {
struct pcm base;
snd_pcm_t *pcm;
int index;
int frames;
int r;
int c;
};
int read_alsa(struct pcm *pcm, short *buff, int frames)
{
struct alsa *alsa = (struct alsa *)(pcm->data);
int got = 0;
while (0 < frames) {
while ((got = snd_pcm_readi(alsa->pcm, buff, frames)) < 0)
if (snd_pcm_prepare(alsa->pcm) < 0)
return 0;
buff += got * alsa->c;
frames -= got;
}
return 1;
}
int open_alsa_read(struct pcm **p, char *name)
{
snd_pcm_t *pcm;
if (snd_pcm_open(&pcm, name, SND_PCM_STREAM_CAPTURE, 0) < 0) {
fprintf(stderr, "Error opening PCM device %s\n", name);
return 0;
}
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(¶ms);
if (snd_pcm_hw_params_any(pcm, params) < 0) {
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(pcm);
return 0;
}
if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
fprintf(stderr, "Error setting access.\n");
snd_pcm_close(pcm);
return 0;
}
if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) {
fprintf(stderr, "Error setting S16_LE format.\n");
snd_pcm_close(pcm);
return 0;
}
if (snd_pcm_hw_params_set_rate_resample(pcm, params, 0) < 0) {
fprintf(stderr, "Error disabling resampling.\n");
snd_pcm_close(pcm);
return 0;
}
unsigned rate_min = 8000;
int dir_min = 0;
if (snd_pcm_hw_params_set_rate_min(pcm, params, &rate_min, &dir_min) < 0 || rate_min < 8000) {
fprintf(stderr, "Error setting min rate.\n");
snd_pcm_close(pcm);
return 0;
}
if (snd_pcm_hw_params(pcm, params) < 0) {
fprintf(stderr, "Error setting HW params.\n");
snd_pcm_close(pcm);
return 0;
}
unsigned int rate = 0;
if (snd_pcm_hw_params_get_rate(params, &rate, 0) < 0) {
fprintf(stderr, "Error getting rate.\n");
snd_pcm_close(pcm);
return 0;
}
unsigned int channels = 0;
if (snd_pcm_hw_params_get_channels(params, &channels) < 0) {
fprintf(stderr, "Error getting channels.\n");
snd_pcm_close(pcm);
return 0;
}
struct alsa *alsa = (struct alsa *)malloc(sizeof(struct alsa));
alsa->base.rw = read_alsa;
alsa->base.data = (void *)alsa;
alsa->pcm = pcm;
alsa->r = rate;
alsa->c = channels;
alsa->frames = 0;
*p = &(alsa->base);
return 1;
}
alsa.h
struct pcm {
int (*rw)(struct pcm *, short *, int);
void *data;
};
int read_alsa(struct pcm *pcm, short *buff, int frames);
int open_alsa_read(struct pcm **, char *);
hackrf_fm_minimal.cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXIMUM_BUF_LENGTH (16 * 16384)
using namespace std;
static volatile bool do_exit = false;
hackrf_device *device;
uint32_t freq;
uint32_t hardware_sample_rate;
uint16_t buf16[MAXIMUM_BUF_LENGTH];
int16_t lowpassed[MAXIMUM_BUF_LENGTH];
int lp_len;
int rate_in;
int rate_out;
int rate_out2;
int16_t result_demod[MAXIMUM_BUF_LENGTH];
int result_demod_len;
int now_r, now_j;
int pre_r, pre_j;
int prev_index;
int now_lpr;
int prev_lpr_index;
FILE *file;
int downsample;
void sigint_callback_handler(int signum)
{
cout << "Caught signal" << endl;
do_exit = true;
}
void multiply(int ar, int aj, int br, int bj, int *cr, int *cj)
{
*cr = ar*br - aj*bj;
*cj = aj*br + ar*bj;
}
int polar_discriminant(int ar, int aj, int br, int bj)
{
int cr, cj;
double angle;
multiply(ar, aj, br, -bj, &cr, &cj);
angle = atan2((double)cj, (double)cr);
return (int)(angle / 3.14159 * (1<<14));
}
int rx_callback(hackrf_transfer* transfer)
{
for (int i = 0; i < transfer->valid_length; i++)
{
double sample = (int8_t)(transfer->buffer[i]) + 1;
buf16[i] = (int16_t)sample; //s->buf16[i] = (int16_t)buf[i] - 127; s->buf16[i] -127~128 uint16_t, unsigned for negative?
}
memcpy(lowpassed, buf16, 2*transfer->valid_length);
lp_len = transfer->valid_length;
//low pass //rate = hardware_sample_rate = 6*2M
int i=0, i2=0;
while (i < lp_len)
{
now_r += lowpassed[i];
now_j += lowpassed[i+1];
i += 2;
prev_index++;
if (prev_index < downsample)
{
continue;
}
lowpassed[i2] = now_r;
lowpassed[i2+1] = now_j;
prev_index = 0;
now_r = 0;
now_j = 0;
i2 += 2;
}
lp_len = i2;
//fm demod //rate = rate_in = 2M
int i3, pcm;
pcm = polar_discriminant(lowpassed[0], lowpassed[1], pre_r, pre_j);
result_demod[0] = (int16_t)pcm;
for (i3 = 2; i3 < (lp_len-1); i3 += 2)
{
pcm = polar_discriminant(lowpassed[i3], lowpassed[i3+1], lowpassed[i3-2], lowpassed[i3-1]);
result_demod[i3/2] = (int16_t)pcm;
}
pre_r = lowpassed[lp_len - 2];
pre_j = lowpassed[lp_len - 1];
result_demod_len = lp_len/2;
// low pass real //rate = rate_out = 2M
int i4=0, i5=0;
int fast = rate_out;
int slow = rate_out2;
while (i4 < result_demod_len)
{
now_lpr += result_demod[i4];
i4++;
prev_lpr_index += slow;
if (prev_lpr_index < fast)
{
continue;
}
result_demod[i5] = (int16_t)(now_lpr / (fast/slow));
prev_lpr_index -= fast;
now_lpr = 0;
i5 += 1;
}
result_demod_len = i5;
//rate = rate_out2 = 8k
fwrite(result_demod, 2, result_demod_len, file);
return 0;
}
int main(int argc, char **argv)
{
signal(SIGINT, &sigint_callback_handler);
int result;
freq = 120e6;
rate_in = 200000;
rate_out = 200000;
rate_out2 = 8000;
file = stdout;
downsample = 6;
hardware_sample_rate = (uint32_t)(downsample * rate_in);
result = hackrf_init();
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_init() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_open(&device);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_open() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_lna_gain(device, 40);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_lna_gain() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_vga_gain(device, 26);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_vga_gain() failed" << endl;
return EXIT_FAILURE;
}
/* Set the frequency */
result = hackrf_set_freq(device, freq);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_freq() failed" << endl;
return EXIT_FAILURE;
}
fprintf(stderr, "Oversampling input by: %ix.\n", downsample);
/* Set the sample rate */
result = hackrf_set_sample_rate(device, hardware_sample_rate);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_sample_rate() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_baseband_filter_bandwidth(device, hardware_sample_rate);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_baseband_filter_bandwidth_set() failed" << endl;
return EXIT_FAILURE;
}
fprintf(stderr, "Output at %u Hz.\n", rate_in);
usleep(100000);
result = hackrf_start_rx(device, rx_callback, NULL);
while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false))
{
usleep(100000);
}
if (do_exit)
{
fprintf(stderr, "\nUser cancel, exiting...\n");
}
else
{
fprintf(stderr, "\nLibrary error, exiting...\n");
}
result = hackrf_close(device);
if(result != HACKRF_SUCCESS)
{
cout << "hackrf_close() failed" << endl;
}
else
{
cout << "hackrf_close() done" << endl;
}
hackrf_exit();
cout << "hackrf_exit() done" << endl;
return 0;
}
g++ hackrf_fm_minimal.cpp -o hackrf_fm -lhackrf -pthread
./hackrf_fm | aplay -r 8k -f S16_LE
g++ decode.cpp alsa.cpp -o decode -lm -lasound `pkg-config --cflags --libs opencv`
./decode
下一步把声音这部分去掉,直接用同一个程序解fm,再解图像。
下面的代码是基于前面的alsa声音读取解码 的例子改的,把关键部分的数据读取改为从stdin接收了
double getSample()
{
double sampleDouble = 0;
static short *pcm_buff;
pcm_buff = (short *)malloc(sizeof(short) * 1);
fread(pcm_buff, sizeof(int16_t), 1, stdin);
/*if (!read_alsa(pcm, pcm_buff, 1))
{
free(pcm_buff);
return 0;
}*/
sampleDouble = ((double)pcm_buff[0]) / 32768.0;
counter = counter + 1;
return bandPassSSTVSignal(sampleDouble);
}
int main(int argc, char **argv)
{
char *pcm_name;
//pcm_name = "default";
frame = Mat::zeros(256, 320, CV_8UC3);
/*if (!open_alsa_read(&pcm, pcm_name))
return 1;*/
while (1)
{
waitForStart();
}
return 0;
}
这是编译和运行的脚本
g++ decode.cpp -o decode -lm `pkg-config --cflags --libs opencv`
./hackrf_fm | ./decode
接下来,我要把程序完全合并,简单起见,我不打算判断header和 vis了,直接不停receiveimage,把这部分工作加入hackrf的rx_callback里。