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 | ||||
| * (quality) consider DC offset calibration | ||||
| * (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