Skip to content

Commit db4eb28

Browse files
iqgen: reworked signal SNR control
Refactored GPS/GLONASS SNR control, added possibility to define signal power in amplitude, power, SNR and SNR(dB) units. Updates SNR estimation due to encoder attenuation. Fixed minor parameter handling issues. New parameters added: '--amplitude-units', '--noise-sigma'. Parameters removed: '--snr'
1 parent 8dd3715 commit db4eb28

16 files changed

+434
-158
lines changed

peregrine/iqgen/bits/amplitude_base.py

Lines changed: 193 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,130 @@
1414
1515
"""
1616

17+
import numpy
18+
19+
20+
class NoiseParameters(object):
21+
'''
22+
Container class for holding noise generation parameters.
23+
'''
24+
25+
def __init__(self, samplingFreqHz, noiseSigma):
26+
'''
27+
Parameters
28+
----------
29+
samplingFreqHz : float or long
30+
Sampling frequency in Hz
31+
noiseSigma : float
32+
Noise Sigma value
33+
'''
34+
super(NoiseParameters, self).__init__()
35+
self.samplingFreqHz = samplingFreqHz
36+
self.noiseSigma = noiseSigma
37+
38+
# Compute coefficient for 1ms integration
39+
self.signalK = noiseSigma * 2. * \
40+
numpy.sqrt(1000000. / float(samplingFreqHz))
41+
42+
self.freqTimeTau = 1e-6 * float(samplingFreqHz)
43+
44+
def getSamplingFreqHz(self):
45+
'''
46+
Get sampling frequency.
47+
48+
Returns
49+
-------
50+
float or long
51+
Sampling frequency in Hz
52+
'''
53+
return self.samplingFreqHz
54+
55+
def getNoiseSigma(self):
56+
'''
57+
Get noise Sigma.
58+
59+
Returns
60+
-------
61+
float
62+
Noise sigma value
63+
'''
64+
return self.noiseSigma
65+
66+
def getFreqTimesTau(self):
67+
'''
68+
Get sampling integration parameter.
69+
70+
Returns
71+
-------
72+
float
73+
Integration parameter of the sampling frequency times integration time.
74+
'''
75+
return self.freqTimeTau
76+
77+
def getSignalK(self):
78+
'''
79+
Get amplification coefficient for SNR at 0 dB.
80+
81+
Returns
82+
-------
83+
float
84+
Signal amplification coefficient for SNR at 0 dB.
85+
'''
86+
return self.signalK
87+
1788

1889
class AmplitudeBase(object):
1990
'''
2091
Amplitude control for a signal source.
92+
93+
Attributes
94+
----------
95+
UNITS_AMPLITUDE : string
96+
Type of object for measuring signal in amplitude. SNR is dependent on
97+
amplitude square.
98+
UNITS_POWER : string
99+
Type of object for measuring signal in power. SNR is linearly dependent on
100+
power.
101+
UNITS_SNR : string
102+
Type of object for measuring signal in SNR.
103+
UNITS_SNR_DB : string
104+
Type of object for measuring signal in SNR dB.
105+
21106
'''
22107

23-
def __init__(self):
108+
UNITS_AMPLITUDE = 'AMP'
109+
UNITS_POWER = 'PWR'
110+
UNITS_SNR = 'SNR'
111+
UNITS_SNR_DB = 'SNR_DB'
112+
113+
def __init__(self, units):
24114
'''
25115
Constructs base object for amplitude control.
116+
117+
Parameters
118+
----------
119+
units : string
120+
Object units. Can be one of the supported values:
121+
- AmplitudeBase::UNITS_AMPLITUDE -- Amplitude units
122+
- AmplitudeBase::UNITS_SNR_DB -- SNR in dB
123+
- AmplitudeBase::UNITS_SNR -- SNR
124+
- AmplitudeBase::UNITS_POWER -- Power units
26125
'''
27126
super(AmplitudeBase, self).__init__()
127+
self.units = units
128+
129+
def getUnits(self):
130+
'''
131+
Provides access to units.
132+
133+
Returns
134+
-------
135+
string
136+
Amplitude units
137+
'''
138+
return self.units
28139

29-
def applyAmplitude(self, signal, userTimeAll_s):
140+
def applyAmplitude(self, signal, userTimeAll_s, noiseParams=None):
30141
'''
31142
Applies amplitude modulation to signal.
32143
@@ -37,6 +148,8 @@ def applyAmplitude(self, signal, userTimeAll_s):
37148
[-1; +1]. This vector is modified in place.
38149
userTimeAll_s : numpy.ndarray
39150
Sample time vector. Each element defines sample time in seconds.
151+
noiseParams : NoiseParameters
152+
Noise parameters to adjust signal amplitude level.
40153
41154
Returns
42155
-------
@@ -45,13 +158,88 @@ def applyAmplitude(self, signal, userTimeAll_s):
45158
'''
46159
raise NotImplementedError()
47160

48-
def computeMeanPower(self):
161+
def computeSNR(self, noiseParams):
49162
'''
50-
Computes mean signal power.
163+
Computes signal to noise ratio in dB.
164+
165+
Parameters
166+
----------
167+
noiseParams : NoiseParameters
168+
Noise parameter container
51169
52170
Returns
53171
-------
54172
float
55-
Mean signal power for the configured amplitude
173+
SNR in dB
56174
'''
57175
raise NotImplementedError()
176+
177+
@staticmethod
178+
def convertUnits2SNR(value, units, noiseParams):
179+
'''
180+
Converts signal units to SNR in dB
181+
182+
Parameters
183+
----------
184+
noiseParams : NoiseParameters
185+
Noise parameter container
186+
187+
Returns
188+
-------
189+
float
190+
SNR in dB
191+
'''
192+
193+
noiseSigma = noiseParams.getNoiseSigma()
194+
freqTimesTau = noiseParams.getFreqTimesTau()
195+
196+
if units == AmplitudeBase.UNITS_AMPLITUDE:
197+
power = numpy.square(value)
198+
snr = freqTimesTau * power / (4. * noiseSigma * noiseSigma)
199+
snrDb = 10 * numpy.log10(snr)
200+
elif units == AmplitudeBase.UNITS_POWER:
201+
power = value
202+
snr = freqTimesTau * power / (4. * noiseSigma * noiseSigma)
203+
snrDb = 10 * numpy.log10(snr)
204+
elif units == AmplitudeBase.UNITS_SNR:
205+
snr = value
206+
snrDb = 10 * numpy.log10(snr)
207+
elif units == AmplitudeBase.UNITS_SNR_DB:
208+
snrDb = value
209+
else:
210+
assert False
211+
return snrDb
212+
213+
@staticmethod
214+
def convertUnits2Amp(value, units, noiseParams):
215+
'''
216+
Converts signal units to amplitude
217+
218+
Parameters
219+
----------
220+
noiseParams : NoiseParameters
221+
Noise parameter container
222+
223+
Returns
224+
-------
225+
float
226+
SNR in dB
227+
'''
228+
229+
noiseSigma = noiseParams.getNoiseSigma()
230+
freqTimesTau = noiseParams.getFreqTimesTau()
231+
232+
if units == AmplitudeBase.UNITS_AMPLITUDE:
233+
amp = value
234+
elif units == AmplitudeBase.UNITS_POWER:
235+
amp = numpy.sqrt(value)
236+
elif units == AmplitudeBase.UNITS_SNR:
237+
snr = value
238+
amp = numpy.sqrt(4. * snr / freqTimesTau) * noiseSigma
239+
elif units == AmplitudeBase.UNITS_SNR_DB:
240+
snrDb = value
241+
snr = 10. ** (0.1 * snrDb)
242+
amp = numpy.sqrt(4. * snr / freqTimesTau) * noiseSigma
243+
else:
244+
assert False
245+
return amp

peregrine/iqgen/bits/amplitude_factory.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,29 @@ def fromMapForm(self, data):
4545
raise ValueError("Invalid object type")
4646

4747
def __PolyAmplitude_ToMap(self, obj):
48-
data = {'type': 'PolyAmplitude', 'coeffs': obj.coeffs}
48+
data = {'type': 'PolyAmplitude',
49+
'coeffs': obj.coeffs,
50+
'units': obj.units}
4951
return data
5052

5153
def __SineAmplitude_ToMap(self, obj):
5254
data = {'type': 'SineAmplitude',
5355
'initial': obj.initial,
5456
'amplitude': obj.amplitude,
55-
'period': obj.period_s}
57+
'period': obj.period_s,
58+
'units': obj.units}
5659
return data
5760

5861
def __MapTo_PolyAmplitude(self, data):
5962
coeffs = data['coeffs']
60-
return PolyAmplitude(coeffs)
63+
units = data['units']
64+
return PolyAmplitude(units, coeffs)
6165

6266
def __MapTo_SineAmplitude(self, data):
6367
initial = data['initial']
6468
amplitude = data['amplitude']
6569
period = data['period']
66-
return SineAmplitude(initial, amplitude, period)
70+
units = data['units']
71+
return SineAmplitude(units, initial, amplitude, period)
6772

6873
factoryObject = ObjectFactory()

peregrine/iqgen/bits/amplitude_poly.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ class AmplitudePoly(AmplitudeBase):
2424
Amplitude control with polynomial dependency over time.
2525
'''
2626

27-
def __init__(self, coeffs):
27+
def __init__(self, units, coeffs):
2828
'''
2929
Constructs polynomial amplitude control object.
3030
3131
Parameters
3232
coeffs : array-like
3333
Polynomial coefficients
3434
'''
35-
super(AmplitudePoly, self).__init__()
35+
super(AmplitudePoly, self).__init__(units)
3636

3737
self.coeffs = tuple([x for x in coeffs])
3838
if len(coeffs) > 0:
@@ -49,9 +49,9 @@ def __str__(self):
4949
string
5050
Literal presentation of object
5151
'''
52-
return "AmplitudePoly(c={})".format(self.coeffs)
52+
return "AmplitudePoly(units={}, c={})".format(self.units, self.coeffs)
5353

54-
def applyAmplitude(self, signal, userTimeAll_s):
54+
def applyAmplitude(self, signal, userTimeAll_s, noiseParams):
5555
'''
5656
Applies amplitude modulation to signal.
5757
@@ -64,6 +64,8 @@ def applyAmplitude(self, signal, userTimeAll_s):
6464
[-1; +1]. This vector is modified in place.
6565
userTimeAll_s : numpy.ndarray
6666
Sample time vector. Each element defines sample time in seconds.
67+
noiseParams : NoiseParameters
68+
Noise parameters to adjust signal amplitude level.
6769
6870
Returns
6971
-------
@@ -74,22 +76,34 @@ def applyAmplitude(self, signal, userTimeAll_s):
7476
poly = self.poly
7577
if poly is not None:
7678
amplitudeVector = poly(userTimeAll_s)
79+
amplitudeVector = AmplitudeBase.convertUnits2Amp(amplitudeVector,
80+
self.units,
81+
noiseParams)
7782
signal *= amplitudeVector
83+
else:
84+
amplitude = AmplitudeBase.convertUnits2Amp(1.,
85+
self.units,
86+
noiseParams)
87+
signal *= amplitude
7888

7989
return signal
8090

81-
def computeMeanPower(self):
91+
def computeSNR(self, noiseParams):
8292
'''
83-
Computes mean signal power.
93+
Computes signal to noise ratio in dB.
94+
95+
noiseParams : NoiseParameters
96+
Noise parameter container
8497
8598
Returns
8699
-------
87100
float
88-
Mean signal power for the configured amplitude
101+
SNR in dB
89102
'''
90103
poly = self.poly
91104
if poly is not None:
92-
result = numpy.square(poly(0.))
105+
value = poly(0.)
93106
else:
94-
result = 1.
95-
return result
107+
value = 1.
108+
109+
return AmplitudeBase.convertUnits2SNR(value, self.units, noiseParams)

0 commit comments

Comments
 (0)