Portapack应用开发教程(十二) SSTV接收机 上

接下来我要讲一个高难度应用,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支持。

所以我接下来的开发计划是:

扫描二维码关注公众号,回复: 11810260 查看本文章

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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <complex.h>
#include <time.h>
#include "alsa.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

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<Vec3b>(y, x)[1] = lum;

            }
            if (linet >= channelBStartInS && linet < channelBStartInS + channelLengthInS) 
            {
                int x = (int)floor(((linet-channelBStartInS)/channelLengthInS)*320);
                frame.at<Vec3b>(y, x)[0] = lum;
            }
            if (linet >= channelRStartInS && linet < channelRStartInS + channelLengthInS)
            {
                int x = (int)floor(((linet-channelRStartInS)/channelLengthInS)*320);
                frame.at<Vec3b>(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 <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#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(&params);
	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 <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <pthread.h>
#include <libhackrf/hackrf.h>
#include <iostream>

#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里。

猜你喜欢

转载自blog.csdn.net/shukebeta008/article/details/106409694