1+ #pylint: disable=trailing-whitespace, line-too-long, bad-whitespace, invalid-name, R0204, C0200
2+ #pylint: disable=superfluous-parens, missing-docstring, broad-except
3+ #pylint: disable=too-many-lines, too-many-instance-attributes, too-many-statements, too-many-nested-blocks
4+ #pylint: disable=too-many-branches, too-many-public-methods, too-many-locals, too-many-arguments
5+
6+ #============================================================================
7+ #RF Explorer Python Libraries - A Spectrum Analyzer for everyone!
8+ #Copyright © 2010-16 Ariel Rocholl, www.rf-explorer.com
9+ #
10+ #This application is free software; you can redistribute it and/or
11+ #modify it under the terms of the GNU Lesser General Public
12+ #License as published by the Free Software Foundation; either
13+ #version 3.0 of the License, or (at your option) any later version.
14+ #
15+ #This software is distributed in the hope that it will be useful,
16+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
17+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18+ #General Public License for more details.
19+ #
20+ #You should have received a copy of the GNU General Public
21+ #License along with this library; if not, write to the Free Software
22+ #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23+ #=============================================================================
24+
25+ class RFEAmplitudeTableData :
26+ """Class support a single collection of calibration amplitude values, of 1 MHz steps
27+ Positive values will be used to externally add to the measurement, that means imply correcting attenuation
28+ whereas negative values will be to externally substract to the measurement, implying correcting gain.
29+ """
30+ CONST_MAX_ENTRY_DATA = 6101
31+ CONST_MIN_ENTRY_DATA = 0
32+ CONST_INVALID_DATA = - 1E10
33+ CONST_DEFAULT_COMPRESSION = - 10.0
34+ CONST_DEFAULT_AMPLITUDE_CORRECTION = 0.0
35+
36+ def __init__ (self ):
37+ self .m_arrAmplitudeCalibrationDataDB = [0.0 ] * self .CONST_MAX_ENTRY_DATA
38+ self .m_arrCompressionDataDBM = [0.0 ] * self .CONST_MAX_ENTRY_DATA
39+ self .m_bHasCompressionData = False
40+ self .lm_bHasCalibrationData = False
41+ self .m_sCalibrationID = ""
42+ self .Clear ()
43+
44+ @property
45+ def CalibrationID (self ):
46+ """Calibration ID is usually a filename to name the calibration in use
47+ future versions may support different IDs than a filename
48+ """
49+ return self .m_sCalibrationID
50+
51+ @property
52+ def HasCompressionData (self ):
53+ """Returns true if data stored include compression amplitude for overload check
54+ """
55+ return self .m_bHasCompressionData
56+
57+ @property
58+ def HasCalibrationData (self ):
59+ """Will return true if there is loaded valid calibration data
60+ """
61+ return self .m_bHasCalibrationData
62+
63+ @classmethod
64+ def FileHeaderVersioned (cls ):
65+ """File header version to control an unknown format
66+
67+ Returns:
68+ String File header version
69+ """
70+ return "--RFEAT01"
71+
72+ def Clear (self ):
73+ """Initialize all collection of calibration amplitude values
74+ """
75+ self .m_sCalibrationID = ""
76+ self .m_bHasCalibrationData = False
77+ for nInd in range (len (self .m_arrAmplitudeCalibrationDataDB )):
78+ self .m_arrAmplitudeCalibrationDataDB [nInd ] = self .CONST_INVALID_DATA
79+ self .m_arrCompressionDataDBM [nInd ] = self .CONST_INVALID_DATA
80+
81+ def GetAmplitudeCalibration (self , nIndexMHz ):
82+ """Amplitude correction data for each MHZ entry
83+
84+ Parameters:
85+ nIndexMHz -- Frequency reference in MHZ to get correction data from
86+ Returns:
87+ Float Correction amplitude value in dB
88+ """
89+ if ((nIndexMHz < self .CONST_MAX_ENTRY_DATA ) and (self .m_arrAmplitudeCalibrationDataDB ) and (range (self .m_arrAmplitudeCalibrationDataDB ) > nIndexMHz ) and (self .m_arrAmplitudeCalibrationDataDB [nIndexMHz ] != self .CONST_INVALID_DATA )):
90+ return self .m_arrAmplitudeCalibrationDataDB [nIndexMHz ]
91+ else :
92+ return self .CONST_DEFAULT_AMPLITUDE_CORRECTION
93+
94+ def GetCompressionAmplitude (self , nIndexMHz ):
95+ """Amplitude compression data for each MHZ entry
96+
97+ Parameters:
98+ nIndexMHz -- Frequency reference in MHZ to get compression amplitude data from
99+ Returns:
100+ Float Compression amplitude value in dB
101+ """
102+ if ((nIndexMHz < self .CONST_MAX_ENTRY_DATA ) and (self .m_arrCompressionDataDBM ) and (range (self .m_arrCompressionDataDBM ) > nIndexMHz ) and (self .m_arrAmplitudeCalibrationDataDB [nIndexMHz ] != self .CONST_INVALID_DATA )):
103+ return self .m_arrCompressionDataDBM [nIndexMHz ]
104+ else :
105+ return self .CONST_DEFAULT_COMPRESSION
106+
107+ def NormalizeDataIterating (self , arrAmplitudeData ):
108+ """Utility function to be used by both arrays, when needed
109+
110+ Parameters:
111+ arrAmplitudeData -- Collection of amplitude calibration data
112+ Returns:
113+ List Collection of amplitude calibration data
114+ """
115+ fAmplitude1 = fAmplitude2 = self .CONST_INVALID_DATA #the two amplitude values to iterate in order to adjust intermediate values
116+ nAmplitude1Ind = nAmplitude2Ind = - 1 #Index used to know the position of the two amplitudes
117+
118+ for nInd in range (len (arrAmplitudeData )):
119+ fVal = arrAmplitudeData [nInd ]
120+
121+ if (fAmplitude1 == self .CONST_INVALID_DATA ):
122+ if (fVal != self .CONST_INVALID_DATA ):
123+ fAmplitude1 = fVal
124+ nAmplitude1Ind = nInd
125+ else :
126+ #use self._DEFAULT_AMPLITUDE_CORRECTION if nothing else is found
127+ #valid yet
128+ arrAmplitudeData [nInd ] = self .CONST_DEFAULT_AMPLITUDE_CORRECTION
129+ elif (fAmplitude2 == self .CONST_INVALID_DATA ):
130+ if (fVal != self .CONST_INVALID_DATA ):
131+ fAmplitude2 = fVal
132+ nAmplitude2Ind = nInd
133+
134+ if ((nAmplitude2Ind - nAmplitude1Ind ) > 1 ):
135+ #if more than one step is between the two, iterate to
136+ #add an incremental delta
137+ fDelta = (fAmplitude2 - fAmplitude1 ) / (nAmplitude2Ind - nAmplitude1Ind )
138+ nSteps = 1
139+ nInd2 = nAmplitude1Ind + 1
140+ while (nInd2 < nAmplitude2Ind ):
141+ arrAmplitudeData [nInd2 ] = fAmplitude1 + nSteps * fDelta
142+ nInd2 += 1
143+ nSteps += 1
144+
145+ fAmplitude1 = fAmplitude2
146+ nAmplitude1Ind = nAmplitude2Ind
147+ fAmplitude2 = self .CONST_INVALID_DATA
148+ nAmplitude2Ind = 0
149+ else :
150+ #Use last valid value from now on, it should be overwritten
151+ #and updated later, but if that was
152+ #the last sample, then this will be good for the remaining
153+ #of the samples
154+ arrAmplitudeData [nInd ] = fAmplitude1
155+
156+ return arrAmplitudeData
157+
158+ def NormalizeAmplitudeCalibrationDataIterating (self ):
159+ """ It will iterate to all values and will fill in anything that is not initialized with a valid value
160+ As oposed to NormalizeDataCopy, it will look for valid values and will fill it in with intermediate
161+ calculated values in between these two. If no valid value is found among two (i.e. last value or first value)
162+ then it is filled in using NormalizedDataCopy.
163+ """
164+ self .m_arrAmplitudeCalibrationDataDB = self .NormalizeDataIterating (self .m_arrAmplitudeCalibrationDataDB )
165+
166+ def NormalizeCompressionData (self ):
167+ """ This function will make sure the compression data has start/end points even if not specified in the file
168+ """
169+ if (self .m_arrCompressionDataDBM [self .CONST_MIN_ENTRY_DATA ] == self .CONST_INVALID_DATA ):
170+ self .m_arrCompressionDataDBM [self .CONST_MIN_ENTRY_DATA ] = self .CONST_DEFAULT_COMPRESSION
171+ if (self .m_arrCompressionDataDBM [self .CONST_MAX_ENTRY_DATA - 1 ] == self .CONST_INVALID_DATA ):
172+ self .m_arrCompressionDataDBM [self .CONST_MAX_ENTRY_DATA - 1 ] = self .CONST_DEFAULT_COMPRESSION
173+
174+ def NormalizeDataCopy (self ):
175+ """It will iterate to all values and will fill in anything that is not initialized with a valid value
176+ It uses a copy method, not an incremental method (i.e. it will pick the first valid value and
177+ go copying the same value over and over till it find another valid one. See NormalizeDataPredict for alternative
178+ """
179+ fLastAmplitude = self .CONST_DEFAULT_AMPLITUDE_CORRECTION
180+ for nInd in range (len (self .m_arrAmplitudeCalibrationDataDB )):
181+ fVal = self .m_arrAmplitudeCalibrationDataDB [nInd ]
182+ if (fVal == self .CONST_INVALID_DATA ):
183+ self .m_arrAmplitudeCalibrationDataDB [nInd ] = fLastAmplitude
184+ else :
185+ fLastAmplitude = fVal
186+
187+ def LoadFile (self , sFilename ):
188+ """Load a file with amplitude and optionally compression data
189+
190+ Parameters:
191+ sFilename -- Full path of the filename
192+ Returns:
193+ Boolean True if everything ok, False if data was invalid
194+ """
195+ bOk = True
196+ try :
197+ with open (sFilename , 'r' ) as objReader :
198+ sHeader = objReader .readline ()[:- 1 ] #[-1] is to delete '\n' at the end
199+ if (sHeader != self .FileHeaderVersioned ()):
200+ #unknown format
201+ return False
202+
203+ self .Clear ()
204+ while (bOk ):
205+ #Read line, trim and replace all consecutive blanks with a single tab
206+ sLine = objReader .readline ().strip (' ' )
207+ sLine = sLine .replace ('\t ' , ' ' )[:- 1 ]
208+ while (" " in sLine ):
209+ sLine = sLine .replace (" " , " " )
210+
211+ if (sLine [:2 ] != "--" ):
212+ arrStrings = sLine .split (' ' )
213+ if (len (arrStrings ) >= 2 ):
214+ nMHZ = int (arrStrings [0 ])
215+ self .m_arrAmplitudeCalibrationDataDB [nMHZ ] = float (arrStrings [1 ])
216+ if (len (arrStrings ) >= 3 ):
217+ #this is a file that includes compression data
218+ self .m_arrCompressionDataDBM [nMHZ ] = float (arrStrings [2 ])
219+ self .m_bHasCompressionData = True
220+ else :
221+ bOk = False
222+
223+ if (bOk ):
224+ #update calibration file name
225+ sFile = sFilename .split ('\\ ' )
226+ if (len (sFile ) > 0 ):
227+ self .m_sCalibrationID = sFile [len (sFile ) - 1 ].ToUpper ().Replace (".RFA" , "" )
228+
229+ #fill in all gaps
230+ self .NormalizeAmplitudeCalibrationDataIterating ()
231+ self .NormalizeCompressionData ()
232+ else :
233+ self .Clear ()
234+ except Exception as obEx :
235+ print ("Error in RFEAmplitudeTableData - LoadFile(): " + str (obEx ))
236+ bOk = False
237+
238+ return bOk
239+
0 commit comments