RxCS Examples
Please note: You can find more RxCS examples in the rxcs/examples folder.
Table of contents:
- Introduction
- 1. Examples – Signal generators
- 2. Examples – Acquisition systems
- 3. Examples – Dictionaries
- 4. Examples – Reconstruction
Introduction
This example shows how to use a Random Multitone Signal Generator (RMSG) module. RMSG is a powerful module which is able to generate random signals, both sparse and dense in the frequency domain. User have broad control over parameters of generated signals. These parameters are:
- Time length of signals
- Representation sampling frequency of signals
- Signal spectrum resolution
- Spectrum content: amplitudes, phases and frequency positions may be given directly or chosen randomly
- Power of the signals
- Noise level in the signals
- The number of signals to be generated
Every module in the RxCS v2.0 toolbox is a Python object. You can read more about Python objects (classes) on the official Python documentation page . RPMG is no different. Firstly, an object must be created, then its parameter are configured by setting object fields. Every object is run by calling run() function. Object’s outputs are accessed by reading fields of the object.
Input and output fields for every object are described in the header of the object. Take a look at RPMG object .
1.1 Signal generators-example #1: Let us generate a multitone signal
Let us walk through an illustrative example of usage of the random multitone signal generator (RMSG). You can find a file with the example in examples/signals in the RxCS package or directly on GitHub .
Firstly, a generator object is created (line 39). Two basic configurations for the generator are the time length of the signals to be generated (line 42) and the signals’ representation sampling frequency (line 43). In this example these are set to 1ms and 1MHz respectively:
38: # Put the generator on board
39: gen = rxcs.sig.randMult()
40:
41: # Settings for the generator
42: gen.tS = 1e-3 # Time of the signal is 1 ms
43: gen.fR = 1e6 # The signal representation sampling frequency is 1 MHz
The lines below set up parameters for the spectrum of signals: the highest possible frequency of a tone and the signals’ spectrum resolution. The highest frequency is set to 10kHz (line 44), so the oversampling ratio is 50 because of the previously set representation sampling frequency. The spectrum resolution parameter is set to 1kHz (line 45), which means that any random tone in the signal can have a frequency from a set as follows: {1kHz, 2kHz, … 10kHz}.
44: gen.fMax = 10e3 # The highest possible frequency in the signal is 10 kHz
45: gen.fRes = 1e3 # The signal spectrum resolution is 1 kHz
There is a space for ten tones in the spectrum of signals to be generated, yet the line below tells the generator to put only one tone into every signal:
47: gen.nTones = 1 # The number of random tones
The generator have a possibility to regulate the power of signals and add a noise to them. The line below sets up the noise so that signal-to-noise ratio is 5dB:
49: gen.iSNR = 5 # The noise added to the signal
Here the generator is started:
52: gen.run()
The generator returns a matrix with generated signals (one signal in a row). The matrix is accessible by mSig field. The lines below extract the generated signal from the first row in the matrix:
53: vSig = gen.mSig[0, :] # Get the generated signal
54: vTSig = gen.vTSig # Get the time vector of the signal
55: fFFTR = gen.fFFTR # Signal FFT frequency resolution
It is worth to note that the generator returns a vector with time samples of the signal (line 54).
We will not analyze rest of the code in the file, it simply analyzes the signal with FFT and plots it in the frequency and in the time domains.
Ok, let’s run it…
Let us run the example and observe its output. When in the examples/signals directory please run the example:
$ python2 randMult_ex0.py
If your console supports colors you will see a nice colored output (image below). Every module from RxCS toolbox reports its name, prints out the parameters it was given and reports execution time. Please note that in the case below the execution time equal to 0.00 means that the real execution time was less then 10ms . To suppress any module from being talkative set a module’s field bVerbose to zero.
The script plots the generated signal in the frequency and the time domains (images below). Please note that the plotted signal is a bit different in every run of the script.
If you have any questions about the example please write to jacek [_at_] pierzchlewski _dot_ com .
1.2 Signal generators-example #2: A more complicated multitone signal
Let us walk through another example of using the random multitone signal generator. You can find a file with the example in examples/signals in the RxCS package or directly on GitHub .
In the previous example the generator did produce a signal with one fully random tone in it. But this example is a bit more complicated – in settings for the generator there is a list of frequencies and amplitudes (but not phases!) of tones which must be always present in produced signals. Phases of these tones are not given, the generator will randomly choose them. Beside of these tones, the generator will add three completely random additional tones to the spectrum.
As in the previous example a generator object is created and basic time/frequency settings are given:
55: # Put the generator on board
56: gen = rxcs.sig.randMult()
57:
58: # Settings for the generator
59: gen.tS = 1e-3 # Time of the signal is 1 ms
60: gen.fR = 1e5 # The signal representation sampling frequency is 100 kHz
Below we give basic parameters of the spectrum. The max frequency of a tone in the spectrum is 40 kHz, the spectrum resolution is 1kHz. Hence. there is a space for 40 tones in the whole spectrum:
61: gen.fMax = 40e3 # The highest possible frequency in the signal is 40 kHz
62: gen.fRes = 1e3 # The signal spectrum resolution is 1 kHz
Here is something new comparing to the previous example. Fields vFrqs, vAmps, vPhs are Numpy vectors with frequencies, amplitudes and phases of tones with the parameters given explicitly. In this example the frequencies and amplitudes are given directly, the amplitude of 2kHz tone equals 1, the amplitude of 3kHz tone equals 2 and the amplitude of 4kHz tone equals 3. The vector with phases has only numpy.nan values, which is an info for the generator that the phases should be chosen randomly:
64: # Frequencies given explicitly
65: gen.vFrqs = np.array([2e3, 3e3, 4e3]) # Vector with given frequencies
66: gen.vAmps = np.array([1, 2, 3]) # Vector with given amplitudes
67: gen.vPhs = np.array([np.nan, np.nan, np.nan]) # Vector with given phases
Beside of the tones given explicitly, the generator will add three completely random tones to the spectrum:
74: gen.nTones = 3 # The number of random tones
Here the boundaries for random amplitudes and phases are given. Lines 88-90 set boundaries for the random amplitudes for three additional random tones, the amplitudes will be randomly selected from the set {0.1, 0.2, 0.3, …, 1.0}. Lines 93-95 set boundaries for the random phases (the values of phases must be given in degrees). Random phases for the three additional tones and for the three tones with frequencies given explicitly will be randomly selected from the set {1, 2, 3, …, 90} degress. The code is below:
79: # Allowed phases in the random tones:
80: gen.iMinPhs = 0 # Minimum phase of random tones
81: gen.iGraPhs = 1 # Gradation of phase of random tones
82: gen.iMaxPhs = 90 # Maximum phase of random tones
And now the generator is started with the configuration given above:
76: # Run the generator and get the output
77: gen.run()
Ok, let’s run it…
Let us run the example. In the examples/signals directory please type:
$ python2 randMult_ex1.py
If your console supports colors you will see a colored output with all the parameters of the generator:
The script plots the generated signal in the frequency domain. You can clearly see three frequencies on 2 kHz, 3 kHz and 4 kHz and three additional tones. Obviously, every time the example is run the three additional tones are placed differently and have different amplitudes, however the amplitudes are always within the boundaries set in lines 88-90.
If you have any questions about the example please write to jacek [ _a t_] pierzchlewski _dot_ com .
2.1 Acquisition systems-example #1: Nonuniform sampling
The previous examples showed how to generate signals, the current example shows how to acquire signals. This example shows how to use a pseudorandom nonuniform sampler with ANGIE sampling scheme. You can read more about the ANGIE sampling scheme in the paper “Generation and Analysis of Constrained Random Sampling Patterns” which is avaialble on arXiv .
You can find a file with the example in examples/acquisition in the RxCS package or directly on GitHub .
41: # Put the stuff on board
42: gen = rxcs.sig.randMult() # Signal generator
43: samp = rxcs.acq.nonuniANGIE() # Sampler
45: # General settings
46: TIME = 1e-3 # Time of the signal is 1 ms
47: FSMP = 1e6 # The signal representation sampling frequency is 1 MHz
Configuration of a signal generator.
Random Multitone Signal Generator (RMSG) is used to generate the signal which will be sampled. The lines 49-54 below, set up the generator parameters. It is a sparse signal with three random tones in the spectrum. If you do not understand the lines below please go through the examples of using RMSG which are above here and here .
49: # Settings for the generator
50: gen.tS = TIME # Time of the signal is 1 ms
51: gen.fR = FSMP # The signal representation sampling frequency is 1 MHz
52: gen.fMax = 10e3 # The highest possible frequency in the signal is 10 kHz
53: gen.fRes = 1e3 # The signal spectrum resolution is 1 kHz
54: gen.nTones = 3 # The number of random tones
Configuration of a sampler.
Note: if you are not familiar with the idea of random sampling patterns please refer to our paper Generation and Analysis of Constrained Random Sampling Patterns .
Lines 56-60 in the example configure the nonuniform sampler. Line 59 sets the sampling grid period to 1 us, line 60 sets the average sampling frequency to 8 kHz. Time length of random sampling patterns does not have to be given, the generator sets the time length automatically – it equals time length of the signals to be sampled.
Minimum interval between sampling points is not set, so the generator sets it to the default value which equals grid period (1 us in this case). Maximum interval between sampling points is not set either, the default value is infinite, which in practice means that there is no requirement for maximum interval between sampling points.
56: # Generate settings for the sampler
57: samp.tS = TIME # Time of the signal
58: samp.fR = FSMP # The signal representation sampling freuqnecy
59: samp.Tg = 1e-6 # The sampling grid period
60: samp.fSamp = 8e3 # The average sampling frequency
Now all the settings for the signal generator and the signal sampler are in place. Line 64 starts the signal generator module, line 66 starts the sampler module. The generator output is connected to the sampler input (line 65).
63: # Run the multitone signal generator and the sampler
64: gen.run() # Run the generator
65: samp.mSig = gen.mSig # Connect the signal from the generator to the sampler
66: samp.run() # Run the sampler
Lines 68-98 plots the original and the sampled signal in the time domain. Let us take a look on line 73 which extracts the observed signal from the sampler. Line 73 takes a matrix (mObSig) with observed signals from the sampler module. The matrix contains observed signals row-wise: one signal in every row. The number of rows in the matrix equals the number of signals in the matrix (mSig) with signals to be sampled given to the sampler. Because the generator produced only one signal, there is only one row in the matrix mObSig.
73: vObSig = samp.mObSig[0, :] # The signal from the generator
Finally, let us take a look on lines 100-114. These lines show how to use observation matrices which are returned by the sampler. An observation matrix is a matrix which is a mathematical representation of a process of sampling. An observed signal is a dot product of an original signal and an observation matrix. The sampler returns a list with observation matrices for signals which were sampled. Line 105 takes the first observation matrix, the one applied to the first and the only signal in the example. Line 106 applies the matrix onto the original signal being sampled and creates an observed signal vObSigPhi.
100: # -----------------------------------------------------------------
101: # APPENDIX:
102: # This part is to show how to use the observation matrix, if it is needed
103: # (for example in compressed sensing systems)
104:
105: mPhi = samp.lPhi[0] # Get the observation matrix (1st element of the list with observation matrices)
106: vObSigPhi = np.dot(mPhi, vSig) # Sample the signal using the observation matrix
107:
108: # Plot the signal and the observed sampling points
109: hFig2 = plt.figure(2)
110: hSubPlot1 = hFig2.add_subplot(111)
111: hSubPlot1.grid(True)
112: hSubPlot1.set_title('Signal and the observed sampling points')
113: hSubPlot1.plot(vT, vSig, '-')
114: hSubPlot1.plot(vPattsT, vObSigPhi, 'ro', markersize=10)
Ok, let’s run it…
Let us run the example. In the examples/acquistion directory please type:
$ python2 nonuniANGIE_ex0.py
Console output from the exmple is as below. Clearly an output from the signal generator and from the signal sampler can be observed:
The example plots the original signal and the sampling points in the time domain. Furthermore, the sampling pattern is plot:
If you have any questions about the example please write to jap [a with a tail] irfducs _dot_ org .
3.1 Dictionaries-example #1: Small IDFT dictionary
The idea of a dictionary is very important in compressed sensing. A dictionary transforms signal from one domain/domains into another. A classic example is an IDFT dictionary which transforms signal from the frequency domain into the time domain. An example of using the module which generates IDFT dictionary is given below.
IDFT dictionary which is generated by the module is a row-wise matrix: one row of a dictionary matrix corresponds to one frequency of a tone in the spectrum. IDFT dictionary is frequency symetrical – there are positive-frequency tones as well as negative-freuqency tones in the dictionary.
Configuration of a dictionary:
Lines 32-33 set up the basic time domain parameters of the dictionary. Time covered by the dictionary is 1 ms, the signal representation frequency is 40 kHz. Lines 34-35 below set up the frequency domain parameters. There are ten tones in the spectrum covered by the dictionary, separation between tones equals 1 kHz:
29: IDFT = rxcs.cs.dict.IDFT() # IDFT dictionary generator
30:
31: # Configure the IDFT generator
32: IDFT.tS = 1e-3 # Time of the dictionary is 1 ms
33: IDFT.fR = 40e3 # Representation sampling frequency is 40 kHz
34: IDFT.fDelta = 1e3 # The frequency separation between tones
35: IDFT.nTones = 10 # The number of tones in the dictionary
36:
Here the IDFT dictionary is generated using the above configuration. The dictionary generator returns dictionary matrix mIDFT.
37: IDFT.run() # Generate the dictionary
Generation of the signal:
Here a signal is generated using the IDFT dictionary. Firstly, vector with frequency coefficients is created (lines 42-45). Spectrum of the dictionary contains ten tones, so there are twenty rows in the dictionary – ten for the positive and ten for the negative frequencies. First row of the dictionary (indexed 0) corresponds to the last row (indexed 19), the second row (indexed 1) corresponds to the last but one row (indexed 18) and so on. To generate real signals the coefficients of the corresponding rows must be complex conjugate to each other. Lines 44-45 sets up a vector with two non-zeros coefficients. The non-zero coefficients correspond to the tones of frequency of 1 kHz in the dictionary. There is no complex part of coefficients, so the signal is composed of a non-shifted cosine tone. Line 51 generates the signal, the signal is a multiplication product of the vector with signal coefficients and the dictionary matrix. Due to numerical inaccuracies it is good to take only real part of the product (line 51). Line 52 reshapes the signal vector to a one-dimensional Numpy vector:
39: # -----------------------------------------------------------------
40: # Generate the signal using the dictionary
41:
42: # Vector with Fourier coefficients
43: vFcoef = np.zeros((1, 20)).astype(complex)
44: vFcoef[0, 0] = 1
45: vFcoef[0, 19] = 1
46:
47: # Get the dictionary matrix
48: mIDFT = IDFT.mDict
49:
50: # Generate a signal and change its shape to a single vector
51: vSig = np.real(np.dot(vFcoef, mIDFT))
52: vSig.shape = (vSig.size,)
Ok, let’s run it…
Let us run the example. In the examples/dictionaries directory please type:
$ python2 dict_IDFT_ex1.p
Console output from the exmple is as below. The dictionary modules prints all its parameters:
The example plots the generated signal. As expected, the signal consists of a single cosine tone:
If you have any questions about the example please write to jacek [_a t_] pierzchlewski _dot_ com .
4.1 Signal reconstruction-example #1: Sparse signal made of cosine tones + L1 minimization
Page last updated: 25h March 2014