Add notes. Start building Python module with useful algorithms.
This commit is contained in:
		
							parent
							
								
									c71a39d3d2
								
							
						
					
					
						commit
						d4e12c75ce
					
				| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This file contains random notitions
 | 
				
			||||||
 | 
					-----------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sample rates between 301000 Hz and 900000 Hz (inclusive) are not supported.
 | 
				
			||||||
 | 
					They cause an invalid configuration of the RTL chip.
 | 
				
			||||||
 | 
					  rsamp_ratio = 28.8 MHz * 2**22 / sample_rate
 | 
				
			||||||
 | 
					  If bit 27 and bit 28 of rsamp_ratio are different, the RTL chip malfunctions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The RTL chip has a configurable 32-tap FIR filter.
 | 
				
			||||||
 | 
					RTL-SDR currently configures it for cutoff at 1.2 MHz (2.4 MS/s).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Casual test of ADC errors:
 | 
				
			||||||
 | 
					 * DC offset in order of 1 code step
 | 
				
			||||||
 | 
					 * I/Q gain mismatch in order of 4%
 | 
				
			||||||
 | 
					 * I/Q phase mismatch in order of 1% of sample interval
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								TODO.txt
								
								
								
								
							
							
						
						
									
										12
									
								
								TODO.txt
								
								
								
								
							| 
						 | 
					@ -1,3 +1,15 @@
 | 
				
			||||||
 | 
					* (experiment) measure raw signal for radio3, radio4 for ~ 1 minute
 | 
				
			||||||
 | 
					   * different gain settings
 | 
				
			||||||
 | 
					   * with/without RTL AGC mode
 | 
				
			||||||
 | 
					   * with several IF gain settings
 | 
				
			||||||
 | 
					* confirm theories about effect of gain, IF gain, AGC
 | 
				
			||||||
 | 
					* look for effect of gain on baseband SNR
 | 
				
			||||||
 | 
					* look for effect of ADC calibration on baseband SNR
 | 
				
			||||||
 | 
					* look for effect of IF bandwidth on baseband SNR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* (experiment) try if RTL2832 FIR filter can be optimized
 | 
				
			||||||
 | 
					* (feature) support 'M' 'k' suffixes for sample rates and tuning frequency
 | 
				
			||||||
 | 
					* (feature) implement off-line FM decoder in Python for experimentation
 | 
				
			||||||
* (feature) implement stereo pilot pulse-per-second
 | 
					* (feature) implement stereo pilot pulse-per-second
 | 
				
			||||||
* (quality) consider DC offset calibration
 | 
					* (quality) consider DC offset calibration
 | 
				
			||||||
* (speedup) maybe replace high-order FIR downsampling filter with 2nd order butterworth followed by lower order FIR filter
 | 
					* (speedup) maybe replace high-order FIR downsampling filter with 2nd order butterworth followed by lower order FIR filter
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										103
									
								
								plltest.py
								
								
								
								
							
							
						
						
									
										103
									
								
								plltest.py
								
								
								
								
							| 
						 | 
					@ -1,103 +0,0 @@
 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
Test PLL algorithm.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Usage: testpll.py baseband.dat centerfreq bandwidth > output.dat
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  baseband.dat   Raw 16-bit signed little-endian sample stream
 | 
					 | 
				
			||||||
  centerfreq     Center frequency relative to sample frequency (0.5 = Nyquist)
 | 
					 | 
				
			||||||
  bandwidth      Approximate bandwidth of PLL relative to sample frequency
 | 
					 | 
				
			||||||
  output.dat     ASCII file with space-separated data
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import numpy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def pll(d, centerfreq, bandwidth):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    minfreq = (centerfreq - bandwidth) * 2 * numpy.pi
 | 
					 | 
				
			||||||
    maxfreq = (centerfreq + bandwidth) * 2 * numpy.pi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    w = bandwidth * 2 * numpy.pi
 | 
					 | 
				
			||||||
    phasor_a = numpy.poly([ numpy.exp(-1.146*w), numpy.exp(-5.331*w) ])
 | 
					 | 
				
			||||||
    phasor_b = numpy.array([ sum(phasor_a) ])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    loopfilter_b = numpy.poly([ numpy.exp(-0.1153*w) ])
 | 
					 | 
				
			||||||
    loopfilter_b *= 0.62 * w
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    n = len(d)
 | 
					 | 
				
			||||||
    y = numpy.zeros(n)
 | 
					 | 
				
			||||||
    phasei = numpy.zeros(n)
 | 
					 | 
				
			||||||
    phaseq = numpy.zeros(n)
 | 
					 | 
				
			||||||
    phaseerr = numpy.zeros(n)
 | 
					 | 
				
			||||||
    freq = numpy.zeros(n)
 | 
					 | 
				
			||||||
    phase = numpy.zeros(n)
 | 
					 | 
				
			||||||
    freq[0] = centerfreq * 2 * numpy.pi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    phasor_i1 = phasor_i2 = 0
 | 
					 | 
				
			||||||
    phasor_q1 = phasor_q2 = 0
 | 
					 | 
				
			||||||
    loopfilter_x1 = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for i in xrange(n):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        psin = numpy.sin(phase[i])
 | 
					 | 
				
			||||||
        pcos = numpy.cos(phase[i])
 | 
					 | 
				
			||||||
        y[i] = pcos
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pi = pcos * d[i]
 | 
					 | 
				
			||||||
        pq = psin * d[i]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pi = phasor_b[0] * pi - phasor_a[1] * phasor_i1 - phasor_a[2] * phasor_i2
 | 
					 | 
				
			||||||
        pq = phasor_b[0] * pq - phasor_a[1] * phasor_q1 - phasor_a[2] * phasor_q2
 | 
					 | 
				
			||||||
        phasor_i2 = phasor_i1
 | 
					 | 
				
			||||||
        phasor_i1 = pi
 | 
					 | 
				
			||||||
        phasor_q2 = phasor_q1
 | 
					 | 
				
			||||||
        phasor_q1 = pq
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        phasei[i] = pi
 | 
					 | 
				
			||||||
        phaseq[i] = pq
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if pi > abs(pq):
 | 
					 | 
				
			||||||
            perr = pq / pi
 | 
					 | 
				
			||||||
        elif pq > 0:
 | 
					 | 
				
			||||||
            perr = 1
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            perr = -1
 | 
					 | 
				
			||||||
        phaseerr[i] = perr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dfreq = loopfilter_b[0] * perr + loopfilter_b[1] * loopfilter_x1
 | 
					 | 
				
			||||||
        loopfilter_x1 = perr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if i + 1 < n:
 | 
					 | 
				
			||||||
            freq[i+1] = min(maxfreq, max(minfreq, freq[i] - dfreq))
 | 
					 | 
				
			||||||
            p = phase[i] + freq[i+1]
 | 
					 | 
				
			||||||
            if p > 2 * numpy.pi:  p -= 2 * numpy.pi
 | 
					 | 
				
			||||||
            if p < -2 * numpy.pi: p += 2 * numpy.pi
 | 
					 | 
				
			||||||
            phase[i+1] = p
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return y, phasei, phaseq, phaseerr, freq, phase
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if len(sys.argv) != 4:
 | 
					 | 
				
			||||||
        print >>sys.stderr, __doc__
 | 
					 | 
				
			||||||
        sys.exit(1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    infile      = sys.argv[1]
 | 
					 | 
				
			||||||
    centerfreq  = float(sys.argv[2])
 | 
					 | 
				
			||||||
    bandwidth   = float(sys.argv[3])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    d = numpy.fromfile(infile, '<i2')
 | 
					 | 
				
			||||||
    d = d.astype(numpy.float64) / 32767.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    (y, phasei, phaseq, phaseerr, freq, phase) = pll(d, centerfreq, bandwidth)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    print '#output phasei, phaseq, phaseerr freq phase'
 | 
					 | 
				
			||||||
    for i in xrange(len(y)):
 | 
					 | 
				
			||||||
        print y[i], phasei[i], phaseq[i], phaseerr[i], freq[i], phase[i]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    main()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,186 @@
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Test lab for FM decoding algorithms.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import types
 | 
				
			||||||
 | 
					import numpy
 | 
				
			||||||
 | 
					import numpy.fft
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def readRawSamples(fname):
 | 
				
			||||||
 | 
					    """Read raw sample file from rtl_sdr."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    d = numpy.fromfile(fname, dtype=numpy.uint8)
 | 
				
			||||||
 | 
					    d = d.astype(numpy.float64)
 | 
				
			||||||
 | 
					    d = (d - 128) / 128.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return d[::2] - 1j * d[1::2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def lazyRawSamples(fname, blocklen):
 | 
				
			||||||
 | 
					    """Return generator over blocks of raw samples."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    f = file(fname, 'rb')
 | 
				
			||||||
 | 
					    while 1:
 | 
				
			||||||
 | 
					        d = f.read(2 * blocklen)
 | 
				
			||||||
 | 
					        if len(d) < 2 * blocklen:
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					        d = numpy.fromstring(d, dtype=numpy.uint8)
 | 
				
			||||||
 | 
					        d = d.astype(numpy.float64)
 | 
				
			||||||
 | 
					        d = (d - 128) / 128.0
 | 
				
			||||||
 | 
					        yield d[::2] - 1j * d[1::2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def freqShiftIQ(d, freqshift):
 | 
				
			||||||
 | 
					    """Shift frequency by multiplication with complex phasor."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def g(d, freqshift):
 | 
				
			||||||
 | 
					        p = 0
 | 
				
			||||||
 | 
					        for b in d:
 | 
				
			||||||
 | 
					            n = len(b)
 | 
				
			||||||
 | 
					            w = numpy.exp((numpy.arange(n) + p) * (2j * numpy.pi * freqshift))
 | 
				
			||||||
 | 
					            p += n
 | 
				
			||||||
 | 
					            yield b * w
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if isinstance(d, types.GeneratorType):
 | 
				
			||||||
 | 
					        return g(d, freqshift)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        n = len(d)
 | 
				
			||||||
 | 
					        w = numpy.exp(numpy.arange(n) * (2j * numpy.pi * freqshift))
 | 
				
			||||||
 | 
					        return d * w
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def spectrum(d, fs=1, nfft=None, sortfreq=False):
 | 
				
			||||||
 | 
					    """Calculate Welch-style power spectral density.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fs       :: sample rate, default to 1
 | 
				
			||||||
 | 
					    nfft     :: FFT length, default to block length
 | 
				
			||||||
 | 
					    sortfreq :: True to put negative freqs in front of positive freqs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Use Hann window with 50% overlap.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Return (freq, Pxx)."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not isinstance(d, types.GeneratorType):
 | 
				
			||||||
 | 
					        d = [ d ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    prev = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if nfft is not None:
 | 
				
			||||||
 | 
					        assert nfft > 0
 | 
				
			||||||
 | 
					        w = numpy.hanning(nfft)
 | 
				
			||||||
 | 
					        q = numpy.zeros(nfft)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pos = 0
 | 
				
			||||||
 | 
					    i = 0
 | 
				
			||||||
 | 
					    for b in d:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if nfft is None:
 | 
				
			||||||
 | 
					            nfft = len(b)
 | 
				
			||||||
 | 
					            assert nfft > 0
 | 
				
			||||||
 | 
					            w = numpy.hanning(nfft)
 | 
				
			||||||
 | 
					            q = numpy.zeros(nfft)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while pos + nfft <= len(b):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if pos < 0:
 | 
				
			||||||
 | 
					                t = numpy.concatenate((prev[pos:], b[:pos+nfft]))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                t = b[pos:pos+nfft]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            t *= w
 | 
				
			||||||
 | 
					            tq = numpy.fft.fft(t)
 | 
				
			||||||
 | 
					            tq *= numpy.conj(tq)
 | 
				
			||||||
 | 
					            q += numpy.real(tq)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            del t
 | 
				
			||||||
 | 
					            del tq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pos += (nfft+(i%2)) // 2
 | 
				
			||||||
 | 
					            i += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pos -= len(b)
 | 
				
			||||||
 | 
					        if pos + len(b) > 0:
 | 
				
			||||||
 | 
					            prev = b
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            prev = numpy.concatenate((prev[pos+len(b):], b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if i > 0:
 | 
				
			||||||
 | 
					        q /= (i * numpy.sum(numpy.square(w)) * fs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    f = numpy.arange(nfft) * (fs / float(nfft))
 | 
				
			||||||
 | 
					    f[nfft//2:] -= fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sortfreq:
 | 
				
			||||||
 | 
					        f = numpy.concatenate((f[nfft//2:], f[:nfft//2]))
 | 
				
			||||||
 | 
					        q = numpy.concatenate((q[nfft//2:], q[:nfft//2]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return f, q
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pll(d, centerfreq, bandwidth):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    minfreq = (centerfreq - bandwidth) * 2 * numpy.pi
 | 
				
			||||||
 | 
					    maxfreq = (centerfreq + bandwidth) * 2 * numpy.pi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    w = bandwidth * 2 * numpy.pi
 | 
				
			||||||
 | 
					    phasor_a = numpy.poly([ numpy.exp(-1.146*w), numpy.exp(-5.331*w) ])
 | 
				
			||||||
 | 
					    phasor_b = numpy.array([ sum(phasor_a) ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loopfilter_b = numpy.poly([ numpy.exp(-0.1153*w) ])
 | 
				
			||||||
 | 
					    loopfilter_b *= 0.62 * w
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    n = len(d)
 | 
				
			||||||
 | 
					    y = numpy.zeros(n)
 | 
				
			||||||
 | 
					    phasei = numpy.zeros(n)
 | 
				
			||||||
 | 
					    phaseq = numpy.zeros(n)
 | 
				
			||||||
 | 
					    phaseerr = numpy.zeros(n)
 | 
				
			||||||
 | 
					    freq = numpy.zeros(n)
 | 
				
			||||||
 | 
					    phase = numpy.zeros(n)
 | 
				
			||||||
 | 
					    freq[0] = centerfreq * 2 * numpy.pi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    phasor_i1 = phasor_i2 = 0
 | 
				
			||||||
 | 
					    phasor_q1 = phasor_q2 = 0
 | 
				
			||||||
 | 
					    loopfilter_x1 = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i in xrange(n):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        psin = numpy.sin(phase[i])
 | 
				
			||||||
 | 
					        pcos = numpy.cos(phase[i])
 | 
				
			||||||
 | 
					        y[i] = pcos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pi = pcos * d[i]
 | 
				
			||||||
 | 
					        pq = psin * d[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pi = phasor_b[0] * pi - phasor_a[1] * phasor_i1 - phasor_a[2] * phasor_i2
 | 
				
			||||||
 | 
					        pq = phasor_b[0] * pq - phasor_a[1] * phasor_q1 - phasor_a[2] * phasor_q2
 | 
				
			||||||
 | 
					        phasor_i2 = phasor_i1
 | 
				
			||||||
 | 
					        phasor_i1 = pi
 | 
				
			||||||
 | 
					        phasor_q2 = phasor_q1
 | 
				
			||||||
 | 
					        phasor_q1 = pq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        phasei[i] = pi
 | 
				
			||||||
 | 
					        phaseq[i] = pq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if pi > abs(pq):
 | 
				
			||||||
 | 
					            perr = pq / pi
 | 
				
			||||||
 | 
					        elif pq > 0:
 | 
				
			||||||
 | 
					            perr = 1
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            perr = -1
 | 
				
			||||||
 | 
					        phaseerr[i] = perr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dfreq = loopfilter_b[0] * perr + loopfilter_b[1] * loopfilter_x1
 | 
				
			||||||
 | 
					        loopfilter_x1 = perr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if i + 1 < n:
 | 
				
			||||||
 | 
					            freq[i+1] = min(maxfreq, max(minfreq, freq[i] - dfreq))
 | 
				
			||||||
 | 
					            p = phase[i] + freq[i+1]
 | 
				
			||||||
 | 
					            if p > 2 * numpy.pi:  p -= 2 * numpy.pi
 | 
				
			||||||
 | 
					            if p < -2 * numpy.pi: p += 2 * numpy.pi
 | 
				
			||||||
 | 
					            phase[i+1] = p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return y, phasei, phaseq, phaseerr, freq, phase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue