├── .gitignore ├── Examples_IOT ├── RFE_Example_IoT_1.py └── RFE_Example_IoT_2.py ├── Examples_USB ├── RFE6GEN_Example_USB.py ├── RFE_Example_USB_1.py ├── RFE_Example_USB_2.py └── RFE_Example_control.py ├── LICENSE ├── README.md └── RFExplorer ├── RFE6GEN_CalibrationData.py ├── RFEAmplitudeTableData.py ├── RFEConfiguration.py ├── RFESweepData.py ├── RFESweepDataCollection.py ├── RFE_Common.py ├── RFExplorer.py ├── ReceiveSerialThread.py └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /Examples_IOT/RFE_Example_IoT_1.py: -------------------------------------------------------------------------------- 1 | #pylint: disable=trailing-whitespace, line-too-long, bad-whitespace, invalid-name, R0204, C0200 2 | #pylint: disable=superfluous-parens, missing-docstring, broad-except, R0801 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 | #This is an example code for RFExplorer python functionality. 8 | #Display amplitude value in dBm and frequency in MHz of the maximum value of sweep data. 9 | #The number of stored sweep data can be configurated by time 10 | #In order to avoid USB issues, connect only RF Explorer Spectrum Analyzer to run this example 11 | #It is not suggested to connect RF Explorer Signal Generator at the same time 12 | #======================================================================================= 13 | 14 | import time 15 | from datetime import datetime, timedelta 16 | import RFExplorer 17 | from RFExplorer import RFE_Common 18 | import math 19 | 20 | #--------------------------------------------------------- 21 | # Helper functions 22 | #--------------------------------------------------------- 23 | 24 | def PrintPeak(objAnalazyer): 25 | """This function prints the amplitude and frequency peak of the latest received sweep 26 | """ 27 | nIndex = objAnalazyer.SweepData.Count-1 28 | objSweepTemp = objAnalazyer.SweepData.GetData(nIndex) 29 | nStep = objSweepTemp.GetPeakDataPoint() #Get index of the peak 30 | fAmplitudeDBM = objSweepTemp.GetAmplitude_DBM(nStep) #Get amplitude of the peak 31 | fCenterFreq = objSweepTemp.GetFrequencyMHZ(nStep) #Get frequency of the peak 32 | fCenterFreq = math.floor(fCenterFreq * 10 ** 3) / 10 ** 3 #truncate to 3 decimals 33 | 34 | print("Sweep[" + str(nIndex)+"]: Peak: " + "{0:.3f}".format(fCenterFreq) + "MHz " + str(fAmplitudeDBM) + "dBm") 35 | 36 | #--------------------------------------------------------- 37 | # global variables and initialization 38 | #--------------------------------------------------------- 39 | 40 | SERIALPORT = None #serial port identifier, use None to autodetect 41 | BAUDRATE = 500000 42 | 43 | objRFE = RFExplorer.RFECommunicator() #Initialize object and thread 44 | TOTAL_SECONDS = 10 #Initialize time span to display activity 45 | 46 | #--------------------------------------------------------- 47 | # Main processing loop 48 | #--------------------------------------------------------- 49 | 50 | try: 51 | #Find and show valid serial ports 52 | objRFE.GetConnectedPorts() 53 | 54 | #Reset IoT board GPIO2 to High Level and GPIO3 to High Level 55 | objRFE.ResetIOT_HW(True) 56 | 57 | #Connect to available port 58 | if (objRFE.ConnectPort(SERIALPORT, BAUDRATE)): 59 | #Wait for unit to notify reset completed 60 | while(objRFE.IsResetEvent): 61 | pass 62 | #Wait for unit to stabilize 63 | time.sleep(3) 64 | 65 | #Request RF Explorer configuration 66 | objRFE.SendCommand_RequestConfigData() 67 | #Wait to receive configuration and model details 68 | while(objRFE.ActiveModel == RFExplorer.RFE_Common.eModel.MODEL_NONE): 69 | objRFE.ProcessReceivedString(True) #Process the received configuration 70 | 71 | #If object is an analyzer, we can scan for received sweeps 72 | if (objRFE.IsAnalyzer()): 73 | print("Receiving data...") 74 | #Process until we complete scan time 75 | nLastDisplayIndex=0 76 | startTime=datetime.now() 77 | while ((datetime.now() - startTime).secondsnLastDisplayIndex): 82 | PrintPeak(objRFE) 83 | nLastDisplayIndex=objRFE.SweepData.Count 84 | else: 85 | print("Error: Device connected is a Signal Generator. \nPlease, connect a Spectrum Analyzer") 86 | else: 87 | print("Not Connected") 88 | except Exception as obEx: 89 | print("Error: " + str(obEx)) 90 | 91 | #--------------------------------------------------------- 92 | # Close object and release resources 93 | #--------------------------------------------------------- 94 | 95 | objRFE.Close() #Finish the thread and close port 96 | objRFE = None 97 | -------------------------------------------------------------------------------- /Examples_IOT/RFE_Example_IoT_2.py: -------------------------------------------------------------------------------- 1 | #pylint: disable=trailing-whitespace, line-too-long, bad-whitespace, invalid-name, R0204, C0200 2 | #pylint: disable=superfluous-parens, missing-docstring, broad-except, R0801 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 | #This is an example code for RFExplorer python functionality. 8 | #Display amplitude in dBm and frequency in MHz of the maximum value of frequency range. 9 | #In order to avoid USB issues, connect only RF Explorer Spectrum Analyzer to run this example 10 | #It is not suggested to connect RF Explorer Signal Generator at the same time 11 | #====================================================================================== 12 | 13 | import time 14 | import RFExplorer 15 | from RFExplorer import RFE_Common 16 | import math 17 | 18 | #--------------------------------------------------------- 19 | # Helper functions 20 | #--------------------------------------------------------- 21 | 22 | def PrintPeak(objAnalazyer): 23 | """This function prints the amplitude and frequency peak of the latest received sweep 24 | """ 25 | nIndex = objAnalazyer.SweepData.Count-1 26 | objSweepTemp = objAnalazyer.SweepData.GetData(nIndex) 27 | nStep = objSweepTemp.GetPeakDataPoint() #Get index of the peak 28 | fAmplitudeDBM = objSweepTemp.GetAmplitude_DBM(nStep) #Get amplitude of the peak 29 | fCenterFreq = objSweepTemp.GetFrequencyMHZ(nStep) #Get frequency of the peak 30 | fCenterFreq = math.floor(fCenterFreq * 10 ** 3) / 10 ** 3 #truncate to 3 decimals 31 | 32 | print(" Peak: " + "{0:.3f}".format(fCenterFreq) + "MHz " + str(fAmplitudeDBM) + "dBm") 33 | 34 | def ControlSettings(objAnalazyer): 35 | """This functions check user settings 36 | """ 37 | SpanSizeTemp = None 38 | StartFreqTemp = None 39 | StopFreqTemp = None 40 | 41 | #print user settings 42 | print("User settings:" + "Span: " + str(SPAN_SIZE_MHZ) +"MHz"+ " - " + "Start freq: " + str(START_SCAN_MHZ) +"MHz"+" - " + "Stop freq: " + str(STOP_SCAN_MHZ) + "MHz") 43 | 44 | #Control maximum Span size 45 | if(objAnalazyer.MaxSpanMHZ <= SPAN_SIZE_MHZ): 46 | print("Max Span size: " + str(objAnalazyer.MaxSpanMHZ)+"MHz") 47 | else: 48 | objAnalazyer.SpanMHZ = SPAN_SIZE_MHZ 49 | SpanSizeTemp = objAnalazyer.SpanMHZ 50 | if(SpanSizeTemp): 51 | #Control minimum start frequency 52 | if(objAnalazyer.MinFreqMHZ > START_SCAN_MHZ): 53 | print("Min Start freq: " + str(objAnalazyer.MinFreqMHZ)+"MHz") 54 | else: 55 | objAnalazyer.StartFrequencyMHZ = START_SCAN_MHZ 56 | StartFreqTemp = objAnalazyer.StartFrequencyMHZ 57 | if(StartFreqTemp): 58 | #Control maximum stop frequency 59 | if(objAnalazyer.MaxFreqMHZ < STOP_SCAN_MHZ): 60 | print("Max Start freq: " + str(objAnalazyer.MaxFreqMHZ)+"MHz") 61 | else: 62 | if((StartFreqTemp + SpanSizeTemp) > STOP_SCAN_MHZ): 63 | print("Max Stop freq (START_SCAN_MHZ + SPAN_SIZE_MHZ): " + str(STOP_SCAN_MHZ) +"MHz") 64 | else: 65 | StopFreqTemp = (StartFreqTemp + SpanSizeTemp) 66 | 67 | return SpanSizeTemp, StartFreqTemp, StopFreqTemp 68 | 69 | #--------------------------------------------------------- 70 | # global variables and initialization 71 | #--------------------------------------------------------- 72 | 73 | SERIALPORT = None #serial port identifier, use None to autodetect 74 | BAUDRATE = 500000 75 | 76 | objRFE = RFExplorer.RFECommunicator() #Initialize object and thread 77 | objRFE.AutoConfigure = False 78 | SPAN_SIZE_MHZ = 100 #Initialize settings 79 | START_SCAN_MHZ = 500 80 | STOP_SCAN_MHZ = 900 81 | 82 | #--------------------------------------------------------- 83 | # Main processing loop 84 | #--------------------------------------------------------- 85 | 86 | try: 87 | #Find and show valid serial ports 88 | objRFE.GetConnectedPorts() 89 | 90 | #Reset IoT board GPIO2 to High Level and GPIO3 to High Level 91 | objRFE.ResetIOT_HW(True) 92 | 93 | #Connect to available port 94 | if (objRFE.ConnectPort(SERIALPORT, BAUDRATE)): 95 | #Wait for unit to notify reset completed 96 | while(objRFE.IsResetEvent): 97 | pass 98 | #Wait for unit to stabilize 99 | time.sleep(3) 100 | 101 | #Request RF Explorer configuration 102 | objRFE.SendCommand_RequestConfigData() 103 | #Wait to receive configuration and model details 104 | while(objRFE.ActiveModel == RFExplorer.RFE_Common.eModel.MODEL_NONE): 105 | objRFE.ProcessReceivedString(True) #Process the received configuration 106 | 107 | #If object is an analyzer, we can scan for received sweeps 108 | if(objRFE.IsAnalyzer()): 109 | #Control settings 110 | SpanSize, StartFreq, StopFreq = ControlSettings(objRFE) 111 | if(SpanSize and StartFreq and StopFreq): 112 | nInd = 0 113 | while (True): 114 | #Set new configuration into device 115 | objRFE.UpdateDeviceConfig(StartFreq, StopFreq) 116 | 117 | objSweep=None 118 | #Wait for new configuration to arrive (as it will clean up old sweep data) 119 | while(True): 120 | objRFE.ProcessReceivedString(True); 121 | if (objRFE.SweepData.Count>0): 122 | objSweep=objRFE.SweepData.GetData(objRFE.SweepData.Count-1) 123 | 124 | nInd += 1 125 | print("Freq range["+ str(nInd) + "]: " + str(StartFreq) +" - "+ str(StopFreq) + "MHz" ) 126 | PrintPeak(objRFE) 127 | if(math.fabs(objRFE.StartFrequencyMHZ - StartFreq) <= 0.001): 128 | break 129 | 130 | #set new frequency range 131 | StartFreq = StopFreq 132 | StopFreq = StartFreq + SpanSize 133 | if (StopFreq > STOP_SCAN_MHZ): 134 | StopFreq = STOP_SCAN_MHZ 135 | 136 | if (StartFreq >= StopFreq): 137 | break 138 | else: 139 | print("Error: settings are wrong.\nPlease, change and try again") 140 | else: 141 | print("Error: Device connected is a Signal Generator. \nPlease, connect a Spectrum Analyzer") 142 | else: 143 | print("Not Connected") 144 | except Exception as obEx: 145 | print("Error: " + str(obEx)) 146 | 147 | #--------------------------------------------------------- 148 | # Close object and release resources 149 | #--------------------------------------------------------- 150 | 151 | objRFE.Close() #Finish the thread and close port 152 | objRFE = None 153 | -------------------------------------------------------------------------------- /Examples_USB/RFE6GEN_Example_USB.py: -------------------------------------------------------------------------------- 1 | #pylint: disable=trailing-whitespace, line-too-long, bad-whitespace, invalid-name, R0204, C0200 2 | #pylint: disable=superfluous-parens, missing-docstring, broad-except, R0801 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 | #This is an example code for RF Explorer RF6GEN Signal Generator python functionality. 8 | #Set frequency and power level to generate a frequency SWEEP and CW signal 9 | #in RFE6GEN Signal Generator. 10 | #In order to avoid USB issues, connect only RF Explorer Signal Generator to run this example. 11 | #It is not suggested to connect RF Explorer Spectrum Analyzer at the same time 12 | #===================================================================================== 13 | 14 | import time 15 | import RFExplorer 16 | 17 | 18 | #--------------------------------------------------------- 19 | # global variables and initialization 20 | #--------------------------------------------------------- 21 | 22 | SERIALPORT = None #serial port identifier, use None to autodetect 23 | BAUDRATE = 500000 24 | 25 | objRFEGenerator = RFExplorer.RFECommunicator() #Initialize object and thread 26 | 27 | #--------------------------------------------------------- 28 | # Main processing loop 29 | #--------------------------------------------------------- 30 | 31 | try: 32 | #Find and show valid serial ports 33 | objRFEGenerator.GetConnectedPorts() 34 | 35 | #Connect to available port 36 | if (objRFEGenerator.ConnectPort(SERIALPORT, BAUDRATE)): 37 | #Reset the unit to start fresh 38 | objRFEGenerator.SendCommand("r") 39 | #Wait for unit to notify reset completed 40 | while(objRFEGenerator.IsResetEvent): 41 | pass 42 | #Wait for unit to stabilize 43 | time.sleep(3) 44 | 45 | #Request RF Explorer Generator configuration 46 | objRFEGenerator.SendCommand_RequestConfigData() 47 | #Wait to receive configuration and model details 48 | while(objRFEGenerator.ActiveModel == RFExplorer.RFE_Common.eModel.MODEL_NONE): 49 | objRFEGenerator.ProcessReceivedString(True) #Process the received configuration 50 | 51 | #If object is a generator, we can continue the example 52 | if (objRFEGenerator.IsGenerator()): 53 | #request internal calibration data, if available 54 | objRFEGenerator.SendCommand("Cq") 55 | objRFE6GENCal = objRFEGenerator.GetRFE6GENCal() #Object to manage the calibration data from generator 56 | while (objRFE6GENCal.GetCalSize() < 0): 57 | objRFEGenerator.ProcessReceivedString(True) #Process the received configuration 58 | 59 | #----------- Frequency Sweep Test Section ----------- 60 | #Set Sweep Setting 61 | print("# New SWEEP settings...") 62 | objRFEGenerator.RFGenStartFrequencyMHZ = 400 63 | objRFEGenerator.RFGenStopFrequencyMHZ = 450 64 | objRFEGenerator.RFGenSweepSteps = 25 65 | objRFEGenerator.RFGenStepWaitMS = 200 66 | print("SWEEP Settings = Start:" + str(objRFEGenerator.StartFrequencyMHZ) + "MHz" + " - Stop:" + str(objRFEGenerator.StopFrequencyMHZ) + "MHz" + 67 | " - Steps:" + str(objRFEGenerator.RFGenSweepSteps) + " - Delay:" + str(objRFEGenerator.RFGenStepWaitMS) + "ms" + " - Power:" + str(objRFEGenerator.GetSignalGeneratorEstimatedAmplitude(objRFEGenerator.RFGenCWFrequencyMHZ)) + "dBm") 68 | #Start Frequency Sweep 69 | objRFEGenerator.SendCommand_GeneratorSweepFreq() 70 | #wait 5 seconds 71 | time.sleep(5) 72 | #Stop Frequency Sweep 73 | objRFEGenerator.SendCommand_GeneratorRFPowerOFF() 74 | print("Stop SWEEP") 75 | 76 | #----------- CW Test Section ----------- 77 | #Set CW settings 78 | print("# New CW settings...") 79 | objRFEGenerator.RFGenCWFrequencyMHZ = 500 80 | objRFEGenerator.RFGenHighPowerSwitch = False 81 | objRFEGenerator.RFGenPowerLevel = 3 82 | print("Change CW settings --> Start:" + str(objRFEGenerator.RFGenCWFrequencyMHZ) + "MHz" + " - Power:" + str(objRFEGenerator.GetSignalGeneratorEstimatedAmplitude(objRFEGenerator.RFGenCWFrequencyMHZ)) + "dBm") 83 | #Start CW 84 | objRFEGenerator.SendCommand_GeneratorCW() 85 | time.sleep(5) 86 | #Change CW power 87 | print("# New CW power...") 88 | objRFEGenerator.RFGenPowerLevel = 0 89 | print("Change CW power = Start:" + str(objRFEGenerator.RFGenCWFrequencyMHZ) + "MHz" + " - Power:" + str(objRFEGenerator.GetSignalGeneratorEstimatedAmplitude(objRFEGenerator.RFGenCWFrequencyMHZ)) + "dBm") 90 | #Start new CW 91 | objRFEGenerator.SendCommand_GeneratorCW() 92 | time.sleep(5) 93 | #Change CW Frequency 94 | print("# New CW frequency...") 95 | objRFEGenerator.RFGenCWFrequencyMHZ = 510 96 | print("Change CW frequency = Start:" + str(objRFEGenerator.RFGenCWFrequencyMHZ) + "MHz" + " - Power:" + str(objRFEGenerator.GetSignalGeneratorEstimatedAmplitude(objRFEGenerator.RFGenCWFrequencyMHZ)) + "dBm") 97 | #Start new CW 98 | objRFEGenerator.SendCommand_GeneratorCW() 99 | time.sleep(5) 100 | else: 101 | print("Error: Device connected is a Spectrum Analyzer. \nPlease, connect a Signal Generator") 102 | else: 103 | print("Not Connected") 104 | except Exception as obEx: 105 | print("Error: " + str(obEx)) 106 | 107 | #--------------------------------------------------------- 108 | # Close object and release resources 109 | #--------------------------------------------------------- 110 | 111 | objRFEGenerator.Close() #Finish the thread and close port 112 | objRFEGenerator = None 113 | -------------------------------------------------------------------------------- /Examples_USB/RFE_Example_USB_1.py: -------------------------------------------------------------------------------- 1 | #pylint: disable=trailing-whitespace, line-too-long, bad-whitespace, invalid-name, R0204, C0200 2 | #pylint: disable=superfluous-parens, missing-docstring, broad-except, R0801 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 | #This is an example code for RFExplorer python functionality. 8 | #Display amplitude value in dBm and frequency in MHz of the maximum value of sweep data. 9 | #The number of stored sweep data can be configurated by time 10 | #In order to avoid USB issues, connect only RF Explorer Spectrum Analyzer to run this example 11 | #It is not suggested to connect RF Explorer Signal Generator at the same time 12 | #======================================================================================= 13 | 14 | import time 15 | from datetime import datetime, timedelta 16 | import RFExplorer 17 | from RFExplorer import RFE_Common 18 | import math 19 | 20 | #--------------------------------------------------------- 21 | # Helper functions 22 | #--------------------------------------------------------- 23 | 24 | def PrintPeak(objAnalazyer): 25 | """This function prints the amplitude and frequency peak of the latest received sweep 26 | """ 27 | nIndex = objAnalazyer.SweepData.Count-1 28 | objSweepTemp = objAnalazyer.SweepData.GetData(nIndex) 29 | nStep = objSweepTemp.GetPeakDataPoint() #Get index of the peak 30 | fAmplitudeDBM = objSweepTemp.GetAmplitude_DBM(nStep) #Get amplitude of the peak 31 | fCenterFreq = objSweepTemp.GetFrequencyMHZ(nStep) #Get frequency of the peak 32 | fCenterFreq = math.floor(fCenterFreq * 10 ** 3) / 10 ** 3 #truncate to 3 decimals 33 | 34 | print("Sweep[" + str(nIndex)+"]: Peak: " + "{0:.3f}".format(fCenterFreq) + "MHz " + str(fAmplitudeDBM) + "dBm") 35 | 36 | #--------------------------------------------------------- 37 | # global variables and initialization 38 | #--------------------------------------------------------- 39 | 40 | SERIALPORT = None #serial port identifier, use None to autodetect 41 | BAUDRATE = 500000 42 | 43 | objRFE = RFExplorer.RFECommunicator() #Initialize object and thread 44 | TOTAL_SECONDS = 10 #Initialize time span to display activity 45 | 46 | #--------------------------------------------------------- 47 | # Main processing loop 48 | #--------------------------------------------------------- 49 | 50 | try: 51 | #Find and show valid serial ports 52 | objRFE.GetConnectedPorts() 53 | 54 | #Connect to available port 55 | if (objRFE.ConnectPort(SERIALPORT, BAUDRATE)): 56 | print("Reseting device...") 57 | #Reset the unit to start fresh 58 | objRFE.SendCommand("r") 59 | #Wait for unit to notify reset completed 60 | while(objRFE.IsResetEvent): 61 | pass 62 | #Wait for unit to stabilize 63 | time.sleep(8) 64 | 65 | #Request RF Explorer configuration 66 | objRFE.SendCommand_RequestConfigData() 67 | #Wait to receive configuration and model details 68 | while(objRFE.ActiveModel == RFExplorer.RFE_Common.eModel.MODEL_NONE): 69 | objRFE.ProcessReceivedString(True) #Process the received configuration 70 | 71 | #If object is an analyzer, we can scan for received sweeps 72 | if (objRFE.IsAnalyzer()): 73 | print("---- Spectrum Analyzer Example ----") 74 | print("Receiving data...") 75 | objRFE.SweepData.CleanAll() 76 | #Process until we complete scan time 77 | nLastDisplayIndex=0 78 | startTime=datetime.now() 79 | while ((datetime.now() - startTime).secondsnLastDisplayIndex): 84 | PrintPeak(objRFE) 85 | nLastDisplayIndex=objRFE.SweepData.Count 86 | else: 87 | print("---- Signal Generator Example ----") 88 | objRFE.RFGenCWFrequencyMHZ = 500; 89 | if(objRFE.ExpansionBoardActive): 90 | objRFE.RFGenExpansionPowerDBM = -40 91 | print("CW setting: " + str(objRFE.RFGenExpansionPowerDBM) + " dBm at " + str(objRFE.RFGenCWFrequencyMHZ) + " MHz") 92 | else: 93 | objRFE.RFGenPowerLevel = 0 #low power setting 94 | objRFE.RFGenHighPowerSwitch = False 95 | print("CW setting: " + str(objRFE.GetSignalGeneratorEstimatedAmplitude(objRFE.RFGenCWFrequencyMHZ)) + "dBm" + " at " + str(objRFE.RFGenCWFrequencyMHZ) + " MHz") 96 | 97 | print("CW power ON") 98 | objRFE.SendCommand_GeneratorCW() 99 | time.sleep(5) 100 | print("CW power OFF") 101 | objRFE.SendCommand_GeneratorRFPowerOFF() 102 | else: 103 | print("Not Connected") 104 | except Exception as obEx: 105 | print("Error: " + str(obEx)) 106 | 107 | #--------------------------------------------------------- 108 | # Close object and release resources 109 | #--------------------------------------------------------- 110 | 111 | objRFE.Close() #Finish the thread and close port 112 | objRFE = None 113 | -------------------------------------------------------------------------------- /Examples_USB/RFE_Example_USB_2.py: -------------------------------------------------------------------------------- 1 | #pylint: disable=trailing-whitespace, line-too-long, bad-whitespace, invalid-name, R0204, C0200 2 | #pylint: disable=superfluous-parens, missing-docstring, broad-except, R0801 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 | #This is an example code for RFExplorer python functionality. 8 | #Display amplitude in dBm and frequency in MHz of the maximum value of frequency range. 9 | #In order to avoid USB issues, connect only RF Explorer Spectrum Analyzer to run this example 10 | #It is not suggested to connect RF Explorer Signal Generator at the same time 11 | #====================================================================================== 12 | 13 | import time 14 | import RFExplorer 15 | from RFExplorer import RFE_Common 16 | import math 17 | 18 | #--------------------------------------------------------- 19 | # Helper functions 20 | #--------------------------------------------------------- 21 | 22 | def PrintPeak(objAnalazyer): 23 | """This function prints the amplitude and frequency peak of the latest received sweep 24 | """ 25 | nIndex = objAnalazyer.SweepData.Count-1 26 | objSweepTemp = objAnalazyer.SweepData.GetData(nIndex) 27 | nStep = objSweepTemp.GetPeakDataPoint() #Get index of the peak 28 | fAmplitudeDBM = objSweepTemp.GetAmplitude_DBM(nStep) #Get amplitude of the peak 29 | fCenterFreq = objSweepTemp.GetFrequencyMHZ(nStep) #Get frequency of the peak 30 | fCenterFreq = math.floor(fCenterFreq * 10 ** 3) / 10 ** 3 #truncate to 3 decimals 31 | 32 | print(" Peak: " + "{0:.3f}".format(fCenterFreq) + "MHz " + str(fAmplitudeDBM) + "dBm") 33 | 34 | def ControlSettings(objAnalazyer): 35 | """This functions check user settings 36 | """ 37 | SpanSizeTemp = None 38 | StartFreqTemp = None 39 | StopFreqTemp = None 40 | 41 | #print user settings 42 | print("User settings:" + "Span: " + str(SPAN_SIZE_MHZ) +"MHz"+ " - " + "Start freq: " + str(START_SCAN_MHZ) +"MHz"+" - " + "Stop freq: " + str(STOP_SCAN_MHZ) + "MHz") 43 | 44 | #Control maximum Span size 45 | if(objAnalazyer.MaxSpanMHZ <= SPAN_SIZE_MHZ): 46 | print("Max Span size: " + str(objAnalazyer.MaxSpanMHZ)+"MHz") 47 | else: 48 | objAnalazyer.SpanMHZ = SPAN_SIZE_MHZ 49 | SpanSizeTemp = objAnalazyer.SpanMHZ 50 | if(SpanSizeTemp): 51 | #Control minimum start frequency 52 | if(objAnalazyer.MinFreqMHZ > START_SCAN_MHZ): 53 | print("Min Start freq: " + str(objAnalazyer.MinFreqMHZ)+"MHz") 54 | else: 55 | objAnalazyer.StartFrequencyMHZ = START_SCAN_MHZ 56 | StartFreqTemp = objAnalazyer.StartFrequencyMHZ 57 | if(StartFreqTemp): 58 | #Control maximum stop frequency 59 | if(objAnalazyer.MaxFreqMHZ < STOP_SCAN_MHZ): 60 | print("Max Start freq: " + str(objAnalazyer.MaxFreqMHZ)+"MHz") 61 | else: 62 | if((StartFreqTemp + SpanSizeTemp) > STOP_SCAN_MHZ): 63 | print("Max Stop freq (START_SCAN_MHZ + SPAN_SIZE_MHZ): " + str(STOP_SCAN_MHZ) +"MHz") 64 | else: 65 | StopFreqTemp = (StartFreqTemp + SpanSizeTemp) 66 | 67 | return SpanSizeTemp, StartFreqTemp, StopFreqTemp 68 | 69 | #--------------------------------------------------------- 70 | # global variables and initialization 71 | #--------------------------------------------------------- 72 | 73 | SERIALPORT = None #serial port identifier, use None to autodetect 74 | BAUDRATE = 500000 75 | 76 | objRFE = RFExplorer.RFECommunicator() #Initialize object and thread 77 | objRFE.AutoConfigure = False 78 | 79 | #These values can be limited by specific RF Explorer Spectrum Analyzer model. 80 | #Check RFE SA Comparation chart from www.rf-explorer.com\models to know what 81 | #frequency setting are available for your model 82 | #These freq settings will be updated later in SA condition. 83 | SPAN_SIZE_MHZ = 50 #Initialize settings 84 | START_SCAN_MHZ = 500 85 | STOP_SCAN_MHZ = 900 86 | 87 | #--------------------------------------------------------- 88 | # Main processing loop 89 | #--------------------------------------------------------- 90 | 91 | try: 92 | #Find and show valid serial ports 93 | objRFE.GetConnectedPorts() 94 | 95 | #Connect to available port 96 | if (objRFE.ConnectPort(SERIALPORT, BAUDRATE)): 97 | print("Reseting device...") 98 | #Reset the unit to start fresh 99 | objRFE.SendCommand("r") 100 | #Wait for unit to notify reset completed 101 | while(objRFE.IsResetEvent): 102 | pass 103 | #Wait for unit to stabilize 104 | time.sleep(8) 105 | 106 | #Request RF Explorer configuration 107 | objRFE.SendCommand_RequestConfigData() 108 | 109 | #Wait to receive configuration and model details 110 | while(objRFE.ActiveModel == RFExplorer.RFE_Common.eModel.MODEL_NONE): 111 | objRFE.ProcessReceivedString(True) #Process the received configuration 112 | 113 | #If object is an analyzer, we can scan for received sweeps 114 | if(objRFE.IsAnalyzer()): 115 | print("---- Spectrum Analyzer Example ----") 116 | #update frequency setting. This was added to be compatible with all RFE SA models 117 | START_SCAN_MHZ = objRFE.MinFreqMHZ 118 | STOP_SCAN_MHZ = START_SCAN_MHZ + 200 119 | #SPAN_SIZE_MHZ = 50 is the minimum span available for RF Explorer SA models 120 | 121 | #Control settings 122 | SpanSize, StartFreq, StopFreq = ControlSettings(objRFE) 123 | if(SpanSize and StartFreq and StopFreq): 124 | nInd = 0 125 | while (True): 126 | #Set new configuration into device 127 | objRFE.UpdateDeviceConfig(StartFreq, StopFreq) 128 | 129 | objSweep=None 130 | #Wait for new configuration to arrive (as it will clean up old sweep data) 131 | while(True): 132 | objRFE.ProcessReceivedString(True); 133 | if (objRFE.SweepData.Count>0): 134 | objSweep=objRFE.SweepData.GetData(objRFE.SweepData.Count-1) 135 | 136 | nInd += 1 137 | print("Freq range["+ str(nInd) + "]: " + str(StartFreq) +" - "+ str(StopFreq) + "MHz" ) 138 | PrintPeak(objRFE) 139 | if(math.fabs(objRFE.StartFrequencyMHZ - StartFreq) <= 0.001): 140 | break 141 | 142 | #set new frequency range 143 | StartFreq = StopFreq 144 | StopFreq = StartFreq + SpanSize 145 | if (StopFreq > STOP_SCAN_MHZ): 146 | StopFreq = STOP_SCAN_MHZ 147 | 148 | if (StartFreq >= StopFreq): 149 | break 150 | else: 151 | print("Error: settings are wrong.\nPlease, change and try again") 152 | else: 153 | print("---- Signal Generator Example ----") 154 | #request internal calibration data, if available 155 | objRFE.SendCommand("Cq") 156 | objRFE6GENCal = objRFE.GetRFE6GENCal() #Object to manage the calibration data from generator 157 | while (objRFE6GENCal.GetCalSize() < 0): 158 | objRFE.ProcessReceivedString(True) #Process the received configuration 159 | 160 | objRFE.RFGenCWFrequencyMHZ = 500; 161 | if(objRFE.ExpansionBoardActive): 162 | #Amplitude sweep 163 | objRFE.RFGenExpansionPowerDBM = -40 164 | objRFE.RFGenExpansionPowerStartDBM = -40 165 | objRFE.RFGenExpansionPowerStepDB = 5 166 | objRFE.RFGenExpansionPowerStopDBM = -20 167 | objRFE.RFGenStepWaitMS = 500 168 | sStartDBM = str(objRFE.RFGenExpansionPowerStartDBM) 169 | sStopDBM = str(objRFE.RFGenExpansionPowerStopDBM) 170 | sSteps = str(objRFE.RFGenExpansionPowerStepDB) 171 | else: 172 | objRFE.RFGenStartHighPowerSwitch = False 173 | objRFE.RFGenStopHighPowerSwitch = True 174 | objRFE.RFGenStartPowerLevel = 0 175 | objRFE.RFGenStopPowerLevel = 3 176 | objRFE.RFGenSweepSteps = 5 177 | objRFE.RFGenStepWaitMS = 500 178 | arrAmplitudeDBM = objRFE6GENCal.GetEstimatedAmplitudeArray(objRFE.RFGenCWFrequencyMHZ) 179 | sStartDBM = str(arrAmplitudeDBM[0]) #min 180 | sStopDBM = str(arrAmplitudeDBM[len(arrAmplitudeDBM) - 1]) #max 181 | sSteps = str(objRFE.RFGenSweepSteps) 182 | 183 | 184 | print("Amplitude Sweep Settings = Start:" + sStartDBM + "dBm" + " - Stop:" + sStopDBM + "dBm" + 185 | " - Steps:" + sSteps + " - Delay:" + str(objRFE.RFGenStepWaitMS) + "ms" + " - CW:" + str(objRFE.RFGenCWFrequencyMHZ) + "MHz") 186 | 187 | print("Amplitude sweep ON") 188 | objRFE.SendCommand_GeneratorSweepAmplitude() 189 | time.sleep(5) 190 | objRFE.SendCommand_GeneratorRFPowerOFF() 191 | print("Amplitude sweep OFF") 192 | 193 | time.sleep(2) 194 | 195 | #Frequency sweep 196 | if(objRFE.ExpansionBoardActive): 197 | objRFE.RFGenExpansionPowerDBM = -40 198 | sPowerDBM = " - Power:" + str(objRFE.RFGenExpansionPowerDBM) + "dBm" 199 | else: 200 | objRFE.RFGenHighPowerSwitch = False 201 | objRFE.RFGenPowerLevel = 0 202 | sPowerDBM = " - Power:" + str(objRFE.GetSignalGeneratorEstimatedAmplitude(objRFE.RFGenCWFrequencyMHZ)) + "dBm" 203 | objRFE.RFGenStartFrequencyMHZ = 495.0 204 | objRFE.RFGenStopFrequencyMHZ = 505.0 205 | objRFE.RFGenExpansionPowerDBM = -40.0 206 | objRFE.RFGenSweepSteps = 11 207 | objRFE.RFGenStepWaitMS = 500 208 | 209 | print("Frequency Sweep Settings = Start:" + str(objRFE.StartFrequencyMHZ) + "MHz" + " - Stop:" + str(objRFE.StopFrequencyMHZ) + "MHz" + 210 | " - Steps:" + str(objRFE.RFGenSweepSteps) + " - Delay:" + str(objRFE.RFGenStepWaitMS) + "ms" + sPowerDBM) 211 | 212 | print("Frequency sweep ON") 213 | objRFE.SendCommand_GeneratorSweepFreq() 214 | time.sleep(5) 215 | objRFE.SendCommand_GeneratorRFPowerOFF() 216 | print("Frequency sweep OFF") 217 | else: 218 | print("Not Connected") 219 | except Exception as obEx: 220 | print("Error: " + str(obEx)) 221 | 222 | #--------------------------------------------------------- 223 | # Close object and release resources 224 | #--------------------------------------------------------- 225 | 226 | objRFE.Close() #Finish the thread and close port 227 | objRFE = None 228 | -------------------------------------------------------------------------------- /Examples_USB/RFE_Example_control.py: -------------------------------------------------------------------------------- 1 | #======================================================================================= 2 | #This is an example code for RFExplorer python functionality. 3 | #Contributed by https://github.com/xiaolu1990 - check with author for support 4 | #======================================================================================= 5 | #try: 6 | # while True: 7 | # input_value = input("please enter [G] for next step, [Q] for quit: ") 8 | # if input_value == "G": 9 | # print ("number is", num) 10 | # num += 1 11 | # continue 12 | # # input_value = input("please enter [G] for next step: ") 13 | # elif input_value == "Q": 14 | # break 15 | # else: 16 | # input_value = input("please enter [G] for next step: ") 17 | #except: 18 | # pass 19 | 20 | 21 | #======================================================================================= 22 | 23 | import numpy as np 24 | import time 25 | from datetime import datetime 26 | import RFExplorer 27 | 28 | 29 | def PrintPeak(objAnalazyer): 30 | """This function prints the amplitude and frequency peak of the latest received sweep 31 | """ 32 | nIndex = objAnalazyer.SweepData.Count-1 33 | objSweepTemp = objAnalazyer.SweepData.GetData(nIndex) 34 | nStep = objSweepTemp.GetPeakDataPoint() #Get index of the peak 35 | fAmplitudeDBM = objSweepTemp.GetAmplitude_DBM(nStep) #Get amplitude of the peak 36 | fCenterFreq = objSweepTemp.GetFrequencyMHZ(nStep) #Get frequency of the peak 37 | 38 | print("Sweep[" + str(nIndex)+"]: Peak: " + "{0:.3f}".format(fCenterFreq) + "MHz " + str(fAmplitudeDBM) + "dBm") 39 | 40 | return fAmplitudeDBM 41 | #--------------------------------------------------------- 42 | # global variables and initialization 43 | #--------------------------------------------------------- 44 | 45 | SERIALPORT = None #serial port identifier, use None to autodetect 46 | BAUDRATE = 500000 47 | 48 | objRFE = RFExplorer.RFECommunicator() #Initialize object and thread 49 | TOTAL_SECONDS = 5 #Initialize time span to display activity 50 | 51 | #--------------------------------------------------------- 52 | # Main processing loop 53 | #--------------------------------------------------------- 54 | 55 | 56 | try: 57 | #Find and show valid serial ports 58 | objRFE.GetConnectedPorts() 59 | 60 | #Connect to available port 61 | if (objRFE.ConnectPort(SERIALPORT, BAUDRATE)): 62 | #Reset the unit to start fresh 63 | objRFE.SendCommand("r") 64 | #Wait for unit to notify reset completed 65 | while(objRFE.IsResetEvent): 66 | pass 67 | #Wait for unit to stabilize 68 | time.sleep(3) 69 | 70 | #Request RF Explorer configuration 71 | objRFE.SendCommand_RequestConfigData() 72 | #Wait to receive configuration and model details 73 | while(objRFE.ActiveModel == RFExplorer.RFE_Common.eModel.MODEL_NONE): 74 | objRFE.ProcessReceivedString(True) #Process the received configuration 75 | 76 | while True: 77 | #create a list for saving the recorded values 78 | sample_value = [] 79 | objRFE.ResetInternalBuffers() 80 | input_value = input("please enter [G] for next step, [Q] for quit: ") 81 | if input_value == "G": 82 | #If object is an analyzer, we can scan for received sweeps 83 | if (objRFE.IsAnalyzer()): 84 | print("Receiving data...") 85 | #Process until we complete scan time 86 | nLastDisplayIndex=0 87 | startTime=datetime.now() 88 | while ((datetime.now() - startTime).secondsnLastDisplayIndex): 93 | peak_value = PrintPeak(objRFE) 94 | sample_value.append(peak_value) 95 | nLastDisplayIndex=objRFE.SweepData.Count 96 | print ("The average values of the sampling values is:", round(np.mean(sample_value), 2), "dBm") 97 | else: 98 | print("Error: Device connected is a Signal Generator. \nPlease, connect a Spectrum Analyzer") 99 | continue 100 | elif input_value == "Q": 101 | break 102 | else: 103 | input_value = input("please enter [G] for next step: ") 104 | else: 105 | print("Not Connected") 106 | except Exception as obEx: 107 | print("Error: " + str(obEx)) 108 | 109 | #--------------------------------------------------------- 110 | # Close object and release resources 111 | #--------------------------------------------------------- 112 | 113 | #objRFE.Close() #Finish the thread and close port 114 | objRFE = None 115 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RF Explorer for Python 2 | 3 | RF Explorer libraries and examples for Python 3.5 4 | 5 | For more details, please [visit wiki](https://github.com/RFExplorer/RFExplorer-for-Python/wiki) 6 | -------------------------------------------------------------------------------- /RFExplorer/RFE6GEN_CalibrationData.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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 RFE6GEN_CalibrationData: 26 | """note this is shared with RFEGenTest 27 | """ 28 | def __init__(self): 29 | #actual -30dBm adjusted values read from signal generator 30 | self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM = None #-30dBm 31 | 32 | self.m_arrSignalGeneratorCalRanges_KHZ = [ 33 | 25000, 33000, 41000, 49000, 57000, 65000, 73000, 81000, 34 | 89000, 97000, 100000, 116000, 132000, 148000, 164000, 180000, 35 | 196000, 212000, 228000, 244000, 260000, 276000, 292000, 308000, 36 | 324000, 340000, 356000, 372000, 388000, 404000, 420000, 436000, 37 | 452000, 468000, 484000, 500000, 532000, 564000, 596000, 38 | 628000, 660000, 692000, 724000, 756000, 788000, 820000, 852000, 39 | 884000, 916000, 948000, 980000, 1012000, 1044000, 1076000, 1108000, 40 | 1140000, 1172000, 1204000, 1236000, 1268000, 1300000, 1332000, 1364000, 41 | 1396000, 1428000, 1460000, 1492000, 1524000, 1556000, 1588000, 1620000, 42 | 1652000, 1684000, 1716000, 1748000, 1780000, 1812000, 1844000, 1876000, 43 | 1908000, 1940000, 1972000, 2004000, 2036000, 2068000, 2100000, 2132000, 44 | 2164000, 2196000, 2228000, 2260000, 2292000, 2324000, 2356000, 2388000, 45 | 2420000, 2452000, 2484000, 2516000, 2548000, 2580000, 2612000, 2644000, 46 | 2676000, 2708000, 2740000, 2772000, 2804000, 2836000, 2868000, 2900000, 47 | 2932000, 2964000, 2996000, 3000000, 3064000, 3128000, 3192000, 3256000, 48 | 3320000, 3384000, 3448000, 3512000, 3576000, 3640000, 3704000, 3768000, 49 | 3832000, 3896000, 3960000, 4024000, 4088000, 4152000, 4216000, 4280000, 50 | 4344000, 4408000, 4472000, 4536000, 4600000, 4664000, 4728000, 4792000, 51 | 4856000, 4920000, 4984000, 5048000, 5112000, 5176000, 5240000, 5304000, 52 | 5368000, 5432000, 5496000, 5560000, 5624000, 5688000, 5752000, 5816000, 53 | 5880000, 5944000] 54 | 55 | #pre-defined const values for different power level configurations organized as {L2,L1,L0,H3,H2,H1,H0} 56 | self.m_arrDeltaAmplitude = [ 57 | [-3.23, -6.24, -9.29, 29.72, 26.71, 23.62, 20.68], 58 | [-3.22, -6.25, -9.25, 29.76, 26.85, 23.81, 20.77], 59 | [-3.23, -6.25, -9.26 , 29.89, 26.73, 23.79, 20.78], 60 | [-3.24, -6.29, -10.71, 29.87, 26.82, 23.7 , 20.82], 61 | [-3.23, -6.28, -9.2 , 30.01, 26.9 , 23.87, 20.78], 62 | [-3.24, -6.3 , -10.69, 29.98, 26.85, 23.89, 20.88], 63 | [-3.27, -6.31, -9.3 , 30.07, 26.97, 23.97, 20.91], 64 | [-3.26, -6.31, -9.37 , 30.02, 26.95, 23.91, 20.83], 65 | [-3.26, -6.33, -9.35 , 29.98, 27.03, 23.98, 20.97], 66 | [-3.25, -6.34, -9.37 , 30.06, 26.92, 22.67, 19.48], 67 | [-3.26, -6.34, -10.75, 30.11, 27.01, 23.89, 20.95], 68 | [-3.26, -6.31, -9.38 , 30.22, 27.02, 23.98, 20.88], 69 | [-3.24, -6.31, -9.32 , 30.2 , 27.06, 23.97, 19.51], 70 | [-3.34, -6.34, -9.36 , 30.15, 26.92, 23.79, 20.98], 71 | [-3.27, -6.39, -9.36 , 30.31, 27.02, 23.96, 20.89], 72 | [-3.35, -6.43, -9.49 , 30.26, 26.98, 23.92, 20.9 ], 73 | [-3.27, -6.37, -9.4 , 30.33, 27.05, 23.98, 20.95], 74 | [-3.25, -6.38, -9.32 , 30.37, 27.09, 24.02, 20.94], 75 | [-3.33, -6.4 , -9.39 , 30.25, 25.82, 23.91, 20.82], 76 | [-3.27, -6.3 , -9.36 , 30.27, 27.01, 23.98, 20.94], 77 | [-3.29, -6.35, -9.38 , 30.19, 27.01, 24 , 20.92], 78 | [-4.86, -6.42, -9.43 , 30.12, 26.97, 23.89, 20.78], 79 | [-3.28, -6.35, -9.35 , 30.06, 27.04, 22.34, 20.87], 80 | [-3.34, -6.34, -9.38 , 29.88, 27.01, 23.92, 20.84], 81 | [-3.3 , -6.34, -9.4 , 29.94, 25.35, 23.75, 20.81], 82 | [-3.28, -6.36, -9.45 , 29.96, 26.91, 23.83, 20.73], 83 | [-3.26, -6.34, -9.39 , 29.91, 26.8 , 23.85, 20.8 ], 84 | [-3.31, -6.38, -9.42 , 29.9 , 26.84, 23.82, 20.73], 85 | [-3.29, -6.41, -9.37 , 29.95, 26.83, 23.76, 20.77], 86 | [-3.27, -6.36, -9.42 , 30 , 26.87, 23.8 , 20.72], 87 | [-2.33, -5.37, -9.43 , 31 , 27.77, 24.77, 21.69], 88 | [-3.28, -6.41, -9.38 , 30.03, 26.8 , 23.65, 20.68], 89 | [-3.29, -6.34, -9.38 , 30.13, 26.81, 22.7 , 20.73], 90 | [-3.28, -6.38, -9.42 , 30.17, 26.87, 23.71, 20.67], 91 | [-3.32, -6.35, -9.41 , 30.14, 26.84, 22.64, 20.7 ], 92 | [-3.28, -6.4 , -10.4 , 30.12, 26.84, 23.72, 20.6 ], 93 | [-3.38, -6.46, -9.57 , 29.99, 26.71, 23.64, 20.59], 94 | [-3.33, -6.36, -9.43 , 29.92, 26.8 , 23.67, 20.61], 95 | [-3.28, -6.44, -9.45 , 29.73, 26.75, 23.52, 20.57], 96 | [-3.24, -6.36, -9.52 , 29.54, 26.75, 23.57, 20.46], 97 | [-3.21, -6.38, -9.42 , 29.57, 26.72, 23.51, 20.45], 98 | [-3.22, -6.38, -9.45 , 29.51, 26.66, 23.41, 20.39], 99 | [-3.21, -6.39, -9.46 , 29.56, 26.55, 23.41, 20.39], 100 | [-3.19, -6.38, -9.41 , 29.54, 26.58, 23.42, 20.32], 101 | [-3.18, -6.36, -9.42 , 29.63, 26.53, 23.41, 20.34], 102 | [-3.13, -6.4 , -9.44 , 29.49, 26.61, 23.32, 20.26], 103 | [-3.1 , -6.32, -9.44 , 29.46, 26.61, 23.34, 20.22], 104 | [-3.08, -7.01, -10.07, 29.39, 26.52, 23.33, 20.26], 105 | [-3.14, -6.23, -9.32 , 29.28, 26.55, 23.26, 19.57], 106 | [-3.07, -6.94, -9.24 , 29.21, 25.85, 23.31, 20.32], 107 | [-3.08, -6.83, -9.2 , 29.09, 26.55, 23.33, 20.26], 108 | [-3.03, -6.18, -9.86 , 28.96, 26.42, 23.34, 20.26], 109 | [-2.48, -5.54, -8.62 , 29.56, 26.96, 23.84, 20.8 ], 110 | [-3.03, -6.14, -9.25 , 28.77, 26.36, 23.2 , 20.11], 111 | [-3.06, -6.19, -9.23 , 28.82, 26.32, 23.14, 20.09], 112 | [-3.09, -6.16, -9.2 , 28.71, 26.23, 23.03, 20.1 ], 113 | [-3.62, -6.29, -9.25 , 28.71, 25.6 , 23.07, 20.03], 114 | [-3.09, -6.22, -9.3 , 28.66, 26.1 , 22.97, 19.93], 115 | [-3.12, -6.22, -9.24 , 28.76, 26.09, 22.99, 19.95], 116 | [-3.06, -6.18, -9.26 , 28.75, 26.16, 22.92, 19.93], 117 | [-3.05, -6.19, -9.24 , 28.13, 26.27, 23.03, 19.44], 118 | [-3.11, -6.24, -9.3 , 28.57, 26.39, 23.04, 19.96], 119 | [-3.11, -6.26, -9.36 , 27.88, 26.31, 22.57, 20 ], 120 | [-3.12, -6.29, -9.73 , 28.07, 26.18, 23.06, 19.89], 121 | [-3.22, -6.35, -9.46 , 27.76, 25.85, 22.5 , 19.83], 122 | [-3.13, -6.31, -9.37 , 27.56, 25.18, 22.43, 19.72], 123 | [-3.14, -6.31, -9.4 , 27.47, 25.05, 22.49, 19.47], 124 | [-3.43, -6.32, -9.34 , 27.59, 25.33, 22.46, 19.31], 125 | [-3.06, -6.57, -9.4 , 27.79, 25.44, 22.39, 19.07], 126 | [-2.97, -6.3 , -9.65 , 27.99, 25.61, 22.52, 19.15], 127 | [-2.98, -6.34, -9.35 , 28.07, 25.86, 22.71, 19.64], 128 | [-3.06, -6.58, -9.57 , 27.68, 25.87, 23.07, 19.95], 129 | [-2.8 , -6.35, -9.45 , 27.22, 25.5 , 23.32, 20.07], 130 | [-2.8 , -6.41, -9.5 , 27 , 24.82, 22.95, 19.98], 131 | [-2.9 , -6.47, -9.44 , 26.57, 24.47, 22.74, 19.53], 132 | [-2.58, -6.43, -9.39 , 26.25, 23.91, 22.03, 19.21], 133 | [-2.7 , -6.23, -9.43 , 25.93, 23.68, 21.49, 18.58], 134 | [-2.4 , -5.97, -9.25 , 26.05, 23.63, 21.1 , 18.22], 135 | [-2.37, -5.93, -9.1 , 26.3 , 23.77, 20.94, 18 ], 136 | [-2.41, -5.8 , -9.01 , 26.65, 24.01, 21.11, 17.95], 137 | [-2.39, -5.67, -9.02 , 26.88, 24.26, 21.37, 18.36], 138 | [-2.21, -5.45, -8.71 , 27.02, 24.53, 21.66, 18.69], 139 | [-2.08, -5.21, -8.44 , 26.71, 24.36, 21.54, 18.62], 140 | [-2.04, -5.14, -8.26 , 26.22, 24.02, 21.04, 18.15], 141 | [-2.14, -5.11, -8.46 , 25.44, 23.29, 20.26, 17.39], 142 | [-2 , -5.09, -8.2 , 25.04, 23.01, 19.53, 16.79], 143 | [-1.82, -4.75, -7.94 , 24.93, 22.76, 19.34, 16.25], 144 | [-1.98, -4.87, -8.12 , 24.71, 22.44, 19.05, 16.05], 145 | [-1.91, -4.74, -7.64 , 25.17, 22.95, 19.4 , 16.33], 146 | [-1.79, -4.63, -7.85 , 25.22, 23.04, 19.38, 16.33], 147 | [-1.75, -4.55, -7.73 , 25.08, 23.38, 19.81, 16.6 ], 148 | [-1.36, -4.15, -7.29 , 25.8 , 23.69, 20.17, 16.94], 149 | [-1.92, -4.34, -7.52 , 25.23, 23.45, 19.44, 16.64], 150 | [-1.51, -4.36, -7.8 , 24.65, 23.1 , 19.57, 15.95], 151 | [-1.52, -4.29, -7.89 , 24.03, 22.67, 18.87, 16.04], 152 | [-1.43, -4.12, -7.33 , 23.62, 22.38, 18.61, 15.75], 153 | [-1.73, -4.16, -7.26 , 23.4 , 22.21, 18.83, 15.54], 154 | [-1.32, -4.04, -7.27 , 23.4 , 22.16, 18.72, 15.43], 155 | [-1.24, -3.97, -7.17 , 23.42, 22.09, 18.8 , 15.55], 156 | [-1.27, -3.89, -7.11 , 23.78, 22.48, 19.08, 15.76], 157 | [-1.22, -3.86, -7.06 , 23.93, 22.75, 19.35, 16.09], 158 | [-1.11, -3.77, -7.02 , 23.91, 22.89, 19.63, 16.31], 159 | [-1.14, -3.75, -6.93 , 23.57, 22.82, 19.74, 16.43], 160 | [-0.9 , -3.3 , -6.24 , 23.62, 22.76, 19.97, 16.8 ], 161 | [-0.86, -3.17, -6.16 , 23.26, 22.49, 19.76, 16.6 ], 162 | [-0.76, -3.09, -6.02 , 23.14, 22.22, 19.61, 16.46], 163 | [-2.81, -3.03, -5.8 , 22.99, 22.23, 19.54, 16.35], 164 | [-0.64, -2.93, -5.79 , 23.05, 22.24, 19.53, 16.43], 165 | [-0.53, -2.74, -5.7 , 23.15, 22.39, 19.71, 16.54], 166 | [-0.58, -2.65, -5.59 , 23.34, 22.51, 19.93, 16.74], 167 | [-0.45, -2.53, -5.48 , 23.4 , 21.94, 19.99, 16.94], 168 | [-0.43, -2.49, -5.45 , 23.24, 22.74, 20.26, 17.03], 169 | [-0.28, -2.34, -5.24 , 23.33, 22.63, 20.3 , 17.13], 170 | [-0.35, -2.23, -5.16 , 23.05, 22.53, 20.13, 16.22], 171 | [-0.62, -2.8 , -5.91 , 22.54, 22.19, 19.76, 16.38], 172 | [-0.43, -2.48, -5.63 , 22.21, 21.97, 19.6 , 16.16], 173 | [-0.42, -2.39, -5.42 , 22.08, 21.81, 19.42, 16.08], 174 | [-0.35, -2.3 , -5.27 , 20.9 , 20.71, 19.23, 16.12], 175 | [-0.35, -2.3 , -6.27 , 21.82, 21.59, 19.5 , 16.21], 176 | [-0.41, -2.38, -5.48 , 21.6 , 21.62, 19.62, 16.48], 177 | [-0.27, -2.06, -5.06 , 21.99, 21.88, 20.06, 15.85], 178 | [-0.14, -1.99, -4.95 , 22.1 , 22.11, 20.36, 17.29], 179 | [-0.08, -1.86, -4.98 , 21.74, 21.77, 20.14, 17.14], 180 | [0.06 , -1.69, -4.6 , 21.54, 21.47, 19.8 , 16.84], 181 | [0.11 , -1.62, -4.74 , 21.41, 21.67, 19.94, 16.94], 182 | [-0.03, -1.74, -4.79 , 21.07, 21.39, 19.89, 16.85], 183 | [-0.01, -1.74, -4.77 , 20.52, 20.98, 19.54, 16.48], 184 | [0.07 , -1.72, -4.8 , 19.95, 18.84, 18.84, 15.81], 185 | [-0.02, -1.94, -5.2 , 19.55, 19.97, 18.39, 15.35], 186 | [-0.03, -2.01, -5.32 , 20.06, 20.51, 18.8 , 15.69], 187 | [-0.02, -2 , -5.41 , 20.7 , 21.2 , 19.58, 16.54], 188 | [-1.16, -1.97, -5.4 , 20.48, 21.08, 19.63, 16.65], 189 | [-0.02, -2.05, -5.53 , 19.53, 20.24, 18.55, 15.61], 190 | [0.06 , -1.98, -5.53 , 19.29, 19.89, 18.07, 14.91], 191 | [0.13 , -2 , -5.62 , 19.59, 20.19, 18.29, 15.2 ], 192 | [0.1 , -2.09, -6.67 , 20.09, 20.72, 19 , 15.88], 193 | [0.08 , -2.18, -5.77 , 20.07, 20.81, 19.16, 16.05], 194 | [0 , -2.3 , -6.08 , 19.42, 20.14, 16.68, 15.37], 195 | [0.16 , -2.72, -5.46 , 19.35, 19.68, 17.63, 13.5 ], 196 | [0.22 , -2.01, -5.48 , 19.38, 19.73, 17.46, 14.21], 197 | [0.21 , -4.01, -5.71 , 19.65, 19.97, 17.73, 14.44], 198 | [0.11 , -2.23, -5.85 , 19.72, 20.05, 17.93, 14.57], 199 | [0.22 , -2.26, -5.96 , 19.88, 20.33, 17.98, 14.67], 200 | [0.16 , -2.4 , -6.03 , 19.76, 20.1 , 17.75, 14.34], 201 | [0.19 , -2.33, -6.09 , 19.36, 19.78, 16.6 , 13.83], 202 | [-0.01, -2.49, -6.78 , 19.03, 19.31, 16.71, 13.26], 203 | [0.03 , -2.66, -6.33 , 19.13, 19.28, 16.51, 13 ], 204 | [-0.44, -2.69, -6.36 , 19.63, 19.58, 16.79, 12.71], 205 | [0.03 , -2.65, -6.38 , 19.89, 19.77, 16.98, 13.34], 206 | [0.27 , -2.32, -6.03 , 19.94, 19.38, 16.88, 12.83], 207 | [-0.14, -2.96, -6.67 , 19.03, 18.86, 15.58, 12.27], 208 | [-0.48, -3.93, -8.06 , 17.34, 17.91, 15.3 , 11.71], 209 | [-0.74, -4.13, -8.34 , 17.42, 18.18, 15.62, 12.01], 210 | [-0.65, -4.18, -8.42 , 18.24, 18.57, 15.9 , 12.39], 211 | [-0.58, -4.06, -8.32 , 17.6 , 18.23, 15.58, 11.9 ], 212 | [-0.91, -4.32, -8.46 , 17.26, 17.51, 14.82, 11.07], 213 | [-0.88, -4.14, -8.15 , 17.49, 17.66, 14.81, 11.24], 214 | [-0.74, -4.13, -8.29 , 17.98, 18.09, 15.38, 11.64], 215 | [-0.82, -4.4 , -8.47 , 18.34, 18.53, 15.8 , 12.05], 216 | [-0.97, -4.53, -8.87 , 18.16, 18.39, 15.6 , 11.86], 217 | [-0.92, -4.8 , -9.19 , 17.82, 17.97, 14.92, 11.35]] 218 | 219 | self.arrFirmware = [ 220 | [-6 ,-12,-19,59, 53, 47, 41], 221 | [-6 ,-13,-19,60, 54, 48, 42], 222 | [-6 ,-13,-19,60, 53, 48, 42], 223 | [-6 ,-13,-21,60, 54, 47, 42], 224 | [-6 ,-13,-18,60, 54, 48, 42], 225 | [-6 ,-13,-21,60, 54, 48, 42], 226 | [-7 ,-13,-19,60, 54, 48, 42], 227 | [-7 ,-13,-19,60, 54, 48, 42], 228 | [-7 ,-13,-19,60, 54, 48, 42], 229 | [-6 ,-13,-19,60, 54, 45, 39], 230 | [-7 ,-13,-22,60, 54, 48, 42], 231 | [-7 ,-13,-19,60, 54, 48, 42], 232 | [-6 ,-13,-19,60, 54, 48, 39], 233 | [-7 ,-13,-19,60, 54, 48, 42], 234 | [-7 ,-13,-19,61, 54, 48, 42], 235 | [-7 ,-13,-19,61, 54, 48, 42], 236 | [-7 ,-13,-19,61, 54, 48, 42], 237 | [-7 ,-13,-19,61, 54, 48, 42], 238 | [-7 ,-13,-19,61, 52, 48, 42], 239 | [-7 ,-13,-19,61, 54, 48, 42], 240 | [-7 ,-13,-19,60, 54, 48, 42], 241 | [-10,-13,-19,60, 54, 48, 42], 242 | [-7 ,-13,-19,60, 54, 45, 42], 243 | [-7 ,-13,-19,60, 54, 48, 42], 244 | [-7 ,-13,-19,60, 51, 48, 42], 245 | [-7 ,-13,-19,60, 54, 48, 41], 246 | [-7 ,-13,-19,60, 54, 48, 42], 247 | [-7 ,-13,-19,60, 54, 48, 41], 248 | [-7 ,-13,-19,60, 54, 48, 42], 249 | [-7 ,-13,-19,60, 54, 48, 41], 250 | [-5 ,-11,-19,62, 56, 50, 43], 251 | [-7 ,-13,-19,60, 54, 47, 41], 252 | [-7 ,-13,-19,60, 54, 45, 41], 253 | [-7 ,-13,-19,60, 54, 47, 41], 254 | [-7 ,-13,-19,60, 54, 45, 41], 255 | [-7 ,-13,-21,60, 54, 47, 41], 256 | [-7 ,-13,-19,60, 53, 47, 41], 257 | [-7 ,-13,-19,60, 54, 47, 41], 258 | [-7 ,-13,-19,59, 54, 47, 41], 259 | [-6 ,-13,-19,59, 54, 47, 41], 260 | [-6 ,-13,-19,59, 53, 47, 41], 261 | [-6 ,-13,-19,59, 53, 47, 41], 262 | [-6 ,-13,-19,59, 53, 47, 41], 263 | [-6 ,-13,-19,59, 53, 47, 41], 264 | [-6 ,-13,-19,59, 53, 47, 41], 265 | [-6 ,-13,-19,59, 53, 47, 41], 266 | [-6 ,-13,-19,59, 53, 47, 40], 267 | [-6 ,-14,-20,59, 53, 47, 41], 268 | [-6 ,-12,-19,59, 53, 47, 39], 269 | [-6 ,-14,-18,58, 52, 47, 41], 270 | [-6 ,-14,-18,58, 53, 47, 41], 271 | [-6 ,-12,-20,58, 53, 47, 41], 272 | [-5 ,-11,-17,59, 54, 48, 42], 273 | [-6 ,-12,-19,58, 53, 46, 40], 274 | [-6 ,-12,-18,58, 53, 46, 40], 275 | [-6 ,-12,-18,57, 52, 46, 40], 276 | [-7 ,-13,-19,57, 51, 46, 40], 277 | [-6 ,-12,-19,57, 52, 46, 40], 278 | [-6 ,-12,-18,58, 52, 46, 40], 279 | [-6 ,-12,-19,58, 52, 46, 40], 280 | [-6 ,-12,-18,56, 53, 46, 39], 281 | [-6 ,-12,-19,57, 53, 46, 40], 282 | [-6 ,-13,-19,56, 53, 45, 40], 283 | [-6 ,-13,-19,56, 52, 46, 40], 284 | [-6 ,-13,-19,56, 52, 45, 40], 285 | [-6 ,-13,-19,55, 50, 45, 39], 286 | [-6 ,-13,-19,55, 50, 45, 39], 287 | [-7 ,-13,-19,55, 51, 45, 39], 288 | [-6 ,-13,-19,56, 51, 45, 38], 289 | [-6 ,-13,-19,56, 51, 45, 38], 290 | [-6 ,-13,-19,56, 52, 45, 39], 291 | [-6 ,-13,-19,55, 52, 46, 40], 292 | [-6 ,-13,-19,54, 51, 47, 40], 293 | [-6 ,-13,-19,54, 50, 46, 40], 294 | [-6 ,-13,-19,53, 49, 45, 39], 295 | [-5 ,-13,-19,53, 48, 44, 38], 296 | [-5 ,-12,-19,52, 47, 43, 37], 297 | [-5 ,-12,-19,52, 47, 42, 36], 298 | [-5 ,-12,-18,53, 48, 42, 36], 299 | [-5 ,-12,-18,53, 48, 42, 36], 300 | [-5 ,-11,-18,54, 49, 43, 37], 301 | [-4 ,-11,-17,54, 49, 43, 37], 302 | [-4 ,-10,-17,53, 49, 43, 37], 303 | [-4 ,-10,-17,52, 48, 42, 36], 304 | [-4 ,-10,-17,51, 47, 41, 35], 305 | [-4 ,-10,-16,50, 46, 39, 34], 306 | [-4 ,-10,-16,50, 46, 39, 33], 307 | [-4 ,-10,-16,49, 45, 38, 32], 308 | [-4 ,-9 ,-15,50, 46, 39, 33], 309 | [-4 ,-9 ,-16,50, 46, 39, 33], 310 | [-4 ,-9 ,-15,50, 47, 40, 33], 311 | [-3 ,-8 ,-15,52, 47, 40, 34], 312 | [-4 ,-9 ,-15,50, 47, 39, 33], 313 | [-3 ,-9 ,-16,49, 46, 39, 32], 314 | [-3 ,-9 ,-16,48, 45, 38, 32], 315 | [-3 ,-8 ,-15,47, 45, 37, 32], 316 | [-3 ,-8 ,-15,47, 44, 38, 31], 317 | [-3 ,-8 ,-15,47, 44, 37, 31], 318 | [-2 ,-8 ,-14,47, 44, 38, 31], 319 | [-3 ,-8 ,-14,48, 45, 38, 32], 320 | [-2 ,-8 ,-14,48, 46, 39, 32], 321 | [-2 ,-8 ,-14,48, 46, 39, 33], 322 | [-2 ,-8 ,-14,47, 46, 39, 33], 323 | [-2 ,-7 ,-12,47, 46, 40, 34], 324 | [-2 ,-6 ,-12,47, 45, 40, 33], 325 | [-2 ,-6 ,-12,46, 44, 39, 33], 326 | [-6 ,-6 ,-12,46, 44, 39, 33], 327 | [-1 ,-6 ,-12,46, 44, 39, 33], 328 | [-1 ,-5 ,-11,46, 45, 39, 33], 329 | [-1 ,-5 ,-11,47, 45, 40, 33], 330 | [-1 ,-5 ,-11,47, 44, 40, 34], 331 | [-1 ,-5 ,-11,46, 45, 41, 34], 332 | [-1 ,-5 ,-10,47, 45, 41, 34], 333 | [-1 ,-4 ,-10,46, 45, 40, 32], 334 | [-1 ,-6 ,-12,45, 44, 40, 33], 335 | [-1 ,-5 ,-11,44, 44, 39, 32], 336 | [-1 ,-5 ,-11,44, 44, 39, 32], 337 | [-1 ,-5 ,-11,42, 41, 38, 32], 338 | [-1 ,-5 ,-13,44, 43, 39, 32], 339 | [-1 ,-5 ,-11,43, 43, 39, 33], 340 | [-1 ,-4 ,-10,44, 44, 40, 32], 341 | [0 ,-4 ,-10,44, 44, 41, 35], 342 | [0 ,-4 ,-10,43, 44, 40, 34], 343 | [0 ,-3 ,-9 ,43, 43, 40, 34], 344 | [0 ,-3 ,-9 ,43, 43, 40, 34], 345 | [0 ,-3 ,-10,42, 43, 40, 34], 346 | [0 ,-3 ,-10,41, 42, 39, 33], 347 | [0 ,-3 ,-10,40, 38, 38, 32], 348 | [0 ,-4 ,-10,39, 40, 37, 31], 349 | [0 ,-4 ,-11,40, 41, 38, 31], 350 | [0 ,-4 ,-11,41, 42, 39, 33], 351 | [-2 ,-4 ,-11,41, 42, 39, 33], 352 | [0 ,-4 ,-11,39, 40, 37, 31], 353 | [0 ,-4 ,-11,39, 40, 36, 30], 354 | [0 ,-4 ,-11,39, 40, 37, 30], 355 | [0 ,-4 ,-13,40, 41, 38, 32], 356 | [0 ,-4 ,-12,40, 42, 38, 32], 357 | [0 ,-5 ,-12,39, 40, 33, 31], 358 | [0 ,-5 ,-11,39, 39, 35, 27], 359 | [0 ,-4 ,-11,39, 39, 35, 28], 360 | [0 ,-8 ,-11,39, 40, 35, 29], 361 | [0 ,-4 ,-12,39, 40, 36, 29], 362 | [0 ,-5 ,-12,40, 41, 36, 29], 363 | [0 ,-5 ,-12,40, 40, 36, 29], 364 | [0 ,-5 ,-12,39, 40, 33, 28], 365 | [0 ,-5 ,-14,38, 39, 33, 27], 366 | [0 ,-5 ,-13,38, 39, 33, 26], 367 | [-1 ,-5 ,-13,39, 39, 34, 25], 368 | [0 ,-5 ,-13,40, 40, 34, 27], 369 | [1 ,-5 ,-12,40, 39, 34, 26], 370 | [0 ,-6 ,-13,38, 38, 31, 25], 371 | [-1 ,-8 ,-16,35, 36, 31, 23], 372 | [-1 ,-8 ,-17,35, 36, 31, 24], 373 | [-1 ,-8 ,-17,36, 37, 32, 25], 374 | [-1 ,-8 ,-17,35, 36, 31, 24], 375 | [-2 ,-9 ,-17,35, 35, 30, 22], 376 | [-2 ,-8 ,-16,35, 35, 30, 22], 377 | [-1 ,-8 ,-17,36, 36, 31, 23], 378 | [-2 ,-9 ,-17,37, 37, 32, 24], 379 | [-2 ,-9 ,-18,36, 37, 31, 24], 380 | [-2 ,-10,-18,36, 36, 30, 23]] 381 | 382 | def GetCalSize(self): 383 | """Return the number of calibraton data if any, otherwise -1 384 | 385 | Returns: 386 | Integer the number of calibraton data if any, otherwise -1 387 | """ 388 | if (self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM): 389 | return len(self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM) 390 | else: 391 | return -1 392 | 393 | def DeleteCal(self): 394 | """Delete calibration data collection 395 | """ 396 | self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM = None 397 | 398 | def InitializeCal(self, nSize, sLine): 399 | """Initialize calibration data collection 400 | 401 | Parameters: 402 | nSize -- Size of the collection 403 | sLine -- Line of text to process and create the collection 404 | Returns: 405 | String Embedded calibration Signal Generator data received 406 | """ 407 | sReport = "" 408 | 409 | self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM = [-30.0] * nSize 410 | 411 | if (not sLine): 412 | return sReport 413 | 414 | #Values using 10*delta from the value delivered when compared with 30dBm. 415 | #For instance if value delivered for a frequency is -28.5dBm, that is a +1.5dB difference 416 | #therefore a 1.5*10=15 value. If the value delivered is -33.2 that is a -3.2dB difference 417 | #therefore a -32 value. 418 | 419 | for nInd in range(nSize): 420 | self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM[nInd] = -30.0 + int(ord(sLine[nInd + 3])) / 10.0 421 | if ((nInd % 16) == 0): 422 | sReport += '\n' 423 | sReport += '{:04.1f}'.format(self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM[nInd]) 424 | if (nInd < nSize - 1): 425 | sReport += "," 426 | 427 | return sReport 428 | 429 | def GetEstimatedAmplitudeArray(self, dFrequencyMHZ): 430 | """Return estimated output power level array with 8 different power position for specific frequency 431 | 432 | Parameters: 433 | dFrequencyMHZ -- Frequency of interest 434 | Returns: 435 | List Amplitude array 436 | """ 437 | arrReturn = [] 438 | 439 | if (self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM): 440 | nFreqInd = self.GetClosestFrequencyIndex(dFrequencyMHZ) 441 | if (nFreqInd >= 0): 442 | arrReturn = [0.0] * 8 443 | dValue30DBM = self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM[nFreqInd] 444 | 445 | arrReturn[0] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][2] 446 | arrReturn[1] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][1] 447 | arrReturn[2] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][0] 448 | arrReturn[3] = dValue30DBM 449 | 450 | arrReturn[4] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][6] 451 | arrReturn[5] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][5] 452 | arrReturn[6] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][4] 453 | arrReturn[7] = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][3] 454 | 455 | return arrReturn 456 | 457 | def GetClosestFrequencyIndex(self, dFrequencyMHZ): 458 | """Return the index of the calibration data collection based on specific frequency 459 | 460 | Parameters: 461 | dFrequencyMHZ -- Frequency of interest 462 | Returns: 463 | Integer the closet frequency Index 464 | """ 465 | nFreqInd = 0 466 | if (self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM): 467 | #search by brute force, if this is considered too slow, can be replace by binary search or something else such a hash 468 | for nInd in range(len(self.m_arrSignalGeneratorCalRanges_KHZ) - 1, 0, -1): 469 | dStartFreqMHZ = self.m_arrSignalGeneratorCalRanges_KHZ[nInd] / 1000.0 470 | if (dStartFreqMHZ <= dFrequencyMHZ): 471 | nFreqInd = nInd 472 | break 473 | 474 | return nFreqInd 475 | 476 | def GetEstimatedAmplitude(self, dFrequencyMHZ, bHighPowerSwitch, nPowerLevel): 477 | """Returns best matching amplitude value based on internal -30dBm calibration table, and configured power switch/attenuator 478 | If not available, this returns the estimated value based on hardcoded measured amplitude values 479 | 480 | Parameters: 481 | dFrequencyMHZ -- Frequency of interest 482 | bHighPowerSwitch -- True if it is disable, otherwise False 483 | nPowerLevel -- Power level from min. 4 to max. 0 484 | Returns: 485 | Float Estimated amplitude in dBm 486 | """ 487 | dValueDBM = 0 488 | dValue30DBM = -30 489 | nFreqInd = self.GetClosestFrequencyIndex(dFrequencyMHZ) 490 | 491 | if (self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM): 492 | dValue30DBM = self.m_arrSignalGeneratorEmbeddedCalibrationActual30DBM[nFreqInd] 493 | 494 | if (bHighPowerSwitch): 495 | if(nPowerLevel == 3): 496 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][3] 497 | elif(nPowerLevel == 2): 498 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][4] 499 | elif(nPowerLevel == 1): 500 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][5] 501 | elif(nPowerLevel == 0): 502 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][6] 503 | else: 504 | if(nPowerLevel == 3): 505 | dValueDBM = dValue30DBM #dValue30DBM is correct already 506 | elif(nPowerLevel == 2): 507 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][0] 508 | elif(nPowerLevel == 1): 509 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][1] 510 | elif(nPowerLevel == 0): 511 | dValueDBM = dValue30DBM + self.m_arrDeltaAmplitude[nFreqInd][2] 512 | 513 | return dValueDBM 514 | -------------------------------------------------------------------------------- /RFExplorer/RFEAmplitudeTableData.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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) - 1): 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)-1): 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)-1): 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 | -------------------------------------------------------------------------------- /RFExplorer/RFEConfiguration.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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 | from RFExplorer import RFE_Common 26 | 27 | class RFEConfiguration: 28 | """Store configuration data 29 | """ 30 | def __init__(self, objSource): 31 | self.m_sLineString = "" 32 | if objSource: 33 | self.fStartMHZ = objSource.fStartMHZ 34 | self.fStepMHZ = objSource.fStepMHZ 35 | self.fAmplitudeTopDBM = objSource.fAmplitudeTopDBM 36 | self.fAmplitudeBottomDBM = objSource.fAmplitudeBottomDBM 37 | self.nFreqSpectrumDataPoints = objSource.nFreqSpectrumDataPoints 38 | self.bExpansionBoardActive = objSource.bExpansionBoardActive 39 | self.m_eMode = objSource.m_eMode 40 | self.fMinFreqMHZ = objSource.fMinFreqMHZ 41 | self.fMaxFreqMHZ = objSource.fMaxFreqMHZ 42 | self.fMaxSpanMHZ = objSource.fMaxSpanMHZ 43 | self.fRBWKHZ = objSource.fRBWKHZ 44 | self.fOffset_dB = objSource.fOffset_dB 45 | self.eCalculator = objSource.eCalculator 46 | self.nBaudrate = objSource.nBaudrate 47 | self.eModulations = objSource.eModulations 48 | self.fThresholdDBM = objSource.fThresholdDBM 49 | 50 | self.bRFEGenHighPowerSwitch = objSource.bRFEGenHighPowerSwitch 51 | self.nRFEGenPowerLevel = objSource.nRFEGenPowerLevel 52 | self.fRFEGenCWFreqMHZ = objSource.fRFEGenCWFreqMHZ 53 | self.nRFEGenSweepWaitMS = objSource.nRFEGenSweepWaitMS 54 | self.bRFEGenPowerON = objSource.bRFEGenPowerON 55 | 56 | self.bRFEGenStartHighPowerSwitch = objSource.bRFEGenStartHighPowerSwitch 57 | self.bRFEGenStopHighPowerSwitch = objSource.bRFEGenStopHighPowerSwitch 58 | self.nRFEGenStartPowerLevel = objSource.nRFEGenStartPowerLevel 59 | self.nRFEGenStopPowerLevel = objSource.nRFEGenStopPowerLevel 60 | self.nRFGenSweepPowerSteps = objSource.nRFGenSweepPowerSteps 61 | 62 | self.fRFEGenExpansionPowerStepDBM = objSource.fRFEGenExpansionPowerStepDBM 63 | self.fRFEGenExpansionPowerStartDBM = objSource.fRFEGenExpansionPowerStartDBM 64 | self.fRFEGenExpansionPowerStopDBM = objSource.fRFEGenExpansionPowerStopDBM 65 | else: 66 | self.fStartMHZ = 0.0 67 | self.fStepMHZ = 0.0 68 | self.fAmplitudeTopDBM = 0.0 69 | self.fAmplitudeBottomDBM = 0.0 70 | self.nFreqSpectrumDataPoints = 0 71 | self.bExpansionBoardActive = False 72 | self.m_eMode = RFE_Common.eMode.MODE_NONE 73 | self.fMinFreqMHZ = 0.0 74 | self.fMaxFreqMHZ = 0.0 75 | self.fMaxSpanMHZ = 0.0 76 | self.fRBWKHZ = 0.0 77 | self.fOffset_dB = 0.0 78 | self.nBaudrate = 0 79 | self.eModulations = RFE_Common.eModulation.MODULATION_NONE 80 | self.fThresholdDBM = 0.0 81 | 82 | self.nRFEGenSweepWaitMS = 0 83 | self.bRFEGenHighPowerSwitch = False 84 | self.nRFEGenPowerLevel = 0 85 | self.fRFEGenCWFreqMHZ = 0.0 86 | self.bRFEGenPowerON = False 87 | self.fRFEGenExpansionPowerDBM = -100.0 88 | 89 | self.bRFEGenStartHighPowerSwitch = False 90 | self.bRFEGenStopHighPowerSwitch = False 91 | self.nRFEGenStartPowerLevel = 0 92 | self.nRFEGenStopPowerLevel = 1 93 | self.nRFGenSweepPowerSteps = 0 94 | 95 | self.fRFEGenExpansionPowerStepDBM = 0.25 96 | self.fRFEGenExpansionPowerStartDBM = -100 97 | self.fRFEGenExpansionPowerStopDBM = 10 98 | 99 | self.eCalculator = RFE_Common.eCalculator.UNKNOWN 100 | 101 | @property 102 | def FreqSpectrumSteps(self): 103 | """Get frequency steps that is frequency points - 1 104 | """ 105 | return int(self.nFreqSpectrumDataPoints - 1) 106 | 107 | 108 | @property 109 | def LineString(self): 110 | """Complete string line with all configuration data 111 | """ 112 | return self.m_sLineString 113 | 114 | @property 115 | def Mode(self): 116 | """ Get current operational mode 117 | """ 118 | return self.m_eMode 119 | 120 | def ProcessReceivedString(self, sLine): 121 | """ Process sLine string and store all configuration data 122 | 123 | Parameters: 124 | sLine -- String with the standart configuration expected 125 | Returns: 126 | Boolean True it is possible to process and store configuration, False otherwise 127 | """ 128 | bOk = True 129 | 130 | try: 131 | self.m_sLineString = sLine 132 | 133 | if ((len(sLine) >= 60) and (sLine.startswith("#C2-F:") or sLine.startswith("#C2-f:"))): 134 | #Spectrum Analyzer mode 135 | nPos = 6; 136 | self.fStartMHZ = int(sLine[nPos:(nPos + 7)]) / 1000.0 #Note it comes in KHZ 137 | nPos += 8; 138 | if (sLine[(nPos + 7)] == ','): 139 | self.fStepMHZ = int(sLine[nPos:(nPos + 7)]) / 1000000.0 #Note it comes in HZ 140 | nPos += 8; 141 | elif (sLine[(nPos + 8)] == ','): 142 | self.fStepMHZ = int(sLine[nPos:(nPos + 8)]) / 1000000.0 #Note it comes in HZ 143 | nPos += 9; 144 | self.fAmplitudeTopDBM = int(sLine[nPos:(nPos + 4)]) 145 | nPos += 5; 146 | self.fAmplitudeBottomDBM = int(sLine[nPos:(nPos + 4)]) 147 | nPos += 5; 148 | if (sLine.startswith("#C2-f:")): 149 | self.nFreqSpectrumDataPoints = int(sLine[nPos:(nPos + 5)]) 150 | nPos += 1 151 | else: 152 | self.nFreqSpectrumDataPoints = int(sLine[nPos:(nPos + 4)]) 153 | nPos += 4; #we use this variable to keep state for long step number 154 | 155 | self.bExpansionBoardActive = (sLine[(nPos + 1)] == '1') 156 | nPos += 3 157 | self.m_eMode = RFE_Common.eMode(int(sLine[nPos:(nPos + 3)])) 158 | nPos += 4 159 | self.fMinFreqMHZ = int(sLine[nPos:(nPos + 7)]) / 1000.0 160 | nPos += 8 161 | self.fMaxFreqMHZ = int(sLine[nPos:(nPos + 7)]) / 1000.0 162 | nPos += 8 163 | self.fMaxSpanMHZ = int(sLine[nPos:(nPos + 7)]) / 1000.0 164 | nPos += 8 165 | if (len(sLine)) >= nPos: 166 | self.fRBWKHZ = int(sLine[nPos:(nPos + 5)]) 167 | nPos += 6 168 | if (len(sLine)) >= nPos: 169 | self.fOffset_dB = int(sLine[nPos:(nPos + 4)]) 170 | nPos += 5 171 | if (len(sLine) > nPos): 172 | self.eCalculator = RFE_Common.eCalculator(int(sLine[nPos:(nPos + 3)])) 173 | elif ((len(sLine) >= 29) and (sLine[0:4] == "#C3-")): 174 | #Signal generator CW, SweepFreq and SweepAmp modes 175 | if (sLine[4] == '*'): 176 | self.fStartMHZ = int(sLine[6:13]) / 1000.0 #Note it comes in KHZ 177 | self.fRFEGenCWFreqMHZ = int(sLine[14: 21]) / 1000.0 #Note it comes in KHZ 178 | self.nFreqSpectrumDataPoints = int(sLine[22:26]) + 1 179 | self.fStepMHZ = int(sLine[27:34]) / 1000.0 #Note it comes in KHZ 180 | self.bRFEGenHighPowerSwitch = (sLine[35] == '1') 181 | self.nRFEGenPowerLevel = int(ord(sLine[37]) - 0x30) 182 | self.nRFGenSweepPowerSteps = int(sLine[39:43]) 183 | self.bRFEGenStartHighPowerSwitch = (sLine[44] == '1') 184 | self.nRFEGenStartPowerLevel = int(ord(sLine[46]) - 0x30) 185 | self.bRFEGenStopHighPowerSwitch = (sLine[48] == '1') 186 | self.nRFEGenStopPowerLevel = int(ord(sLine[50]) - 0x30) 187 | self.bRFEGenPowerON = (sLine[52] == '1') 188 | self.nRFEGenSweepWaitMS = int(sLine[54:59]) 189 | self.m_eMode = RFE_Common.eMode.MODE_NONE 190 | elif (sLine[4] == 'A'): 191 | #Sweep Amplitude mode 192 | self.fRFEGenCWFreqMHZ = int(sLine[6:13]) / 1000.0 #Note it comes in KHZ 193 | self.nRFGenSweepPowerSteps = int(sLine[14:18]) 194 | self.bRFEGenStartHighPowerSwitch = (sLine[19] == '1') 195 | self.nRFEGenStartPowerLevel = int(ord(sLine[21]) - 0x30) 196 | self.bRFEGenStopHighPowerSwitch = (sLine[23] == '1') 197 | self.nRFEGenStopPowerLevel = int(ord(sLine[25]) - 0x30) 198 | self.bRFEGenPowerON = (sLine[27] == '1') 199 | self.nRFEGenSweepWaitMS = int(sLine[29:34]) 200 | self.m_eMode = RFE_Common.eMode.MODE_GEN_SWEEP_AMP 201 | elif (sLine[4] == 'F'): 202 | #Sweep Frequency mode 203 | # r'#C3-F:0221000,0020,0000100,0,0,1,00150' 204 | # Positions:6,14,19,27,29,31,33 205 | self.fStartMHZ = int(sLine[6:13]) / 1000.0 #Note it comes in KHZ 206 | self.nFreqSpectrumDataPoints = int(sLine[14:18]) + 1 207 | self.fStepMHZ = int(sLine[19:26]) / 1000.0 #Note it comes in KHZ 208 | self.bRFEGenHighPowerSwitch = (sLine[27] == '1') 209 | self.nRFEGenPowerLevel = int(ord(sLine[29]) - 0x30) 210 | self.bRFEGenPowerON = (sLine[31] == '1') 211 | self.nRFEGenSweepWaitMS = int(sLine[33:38]) 212 | self.m_eMode = RFE_Common.eMode.MODE_GEN_SWEEP_FREQ 213 | elif (sLine[4] == 'G'): 214 | #Normal CW mode 215 | # r'#C3-G:0432144,0432144,0020,0000100,0,0,0' 216 | # Positions:14,22,27,35,37,39 217 | self.fRFEGenCWFreqMHZ = int(sLine[14:21]) / 1000.0 #Note it comes in KHZ 218 | self.nFreqSpectrumDataPoints = int(sLine[22:26]) + 1 219 | self.fStepMHZ = int(sLine[27:34]) / 1000.0 #Note it comes in KHZ 220 | self.bRFEGenHighPowerSwitch = (sLine[35] == '1') 221 | self.nRFEGenPowerLevel = int(ord(sLine[37]) - 0x30) 222 | self.bRFEGenPowerON = (sLine[39] == '1') 223 | self.m_eMode = RFE_Common.eMode.MODE_GEN_CW 224 | else: 225 | self.m_eMode = RFE_Common.eMode.MODE_NONE 226 | bOk = False 227 | elif ((len(sLine) >= 20) and (sLine.startswith("#C5-"))): 228 | #Signal generator CW, SweepFreq and SweepAmp modes 229 | if(sLine[4] == '*') : 230 | self.fStartMHZ = int(sLine[6:13]) / 1000.0 #Note it comes in KHZ 231 | self.fRFEGenCWFreqMHZ = int(sLine[14:21]) / 1000.0 #Note it comes in KHZ 232 | self.nFreqSpectrumDataPoints = int(sLine[22:26]) + 1 233 | self.fStepMHZ = int(sLine[27:34]) / 1000.0 #Note it comes in KHZ 234 | self.fRFEGenExpansionPowerDBM = float(sLine[35:40]) 235 | self.fRFEGenExpansionPowerStepDBM = float(sLine[41:46]) 236 | self.fRFEGenExpansionPowerStartDBM = float(sLine[47:52]) 237 | self.fRFEGenExpansionPowerStopDBM = float(sLine[53:58]) 238 | self.bRFEGenPowerON = (sLine[59] == '1') 239 | self.nRFEGenSweepWaitMS = int(sLine[61:66]) 240 | self.m_eMode = RFE_Common.eMode.MODE_NONE 241 | elif(sLine[4] == 'A'): 242 | #Sweep Amplitude mode 243 | self.fRFEGenCWFreqMHZ = int(sLine[6:13]) / 1000.0 #note it comes in KHZ 244 | self.fRFEGenExpansionPowerStepDBM = float(sLine[14:19]) 245 | self.fRFEGenExpansionPowerStartDBM = float(sLine[20:25]) 246 | self.fRFEGenExpansionPowerStopDBM = float(sLine[26:31]) 247 | self.bRFEGenPowerON = (sLine[32] == '1') 248 | self.nRFEGenSweepWaitMS = int(sLine[34:39]) 249 | self.m_eMode = RFE_Common.eMode.MODE_GEN_SWEEP_AMP 250 | elif(sLine[4] == 'F'): 251 | #Sweep Frequency mode 252 | self.fStartMHZ = int(sLine[6:13]) / 1000.0 #note it comes in KHZ 253 | self.nFreqSpectrumDataPoints = int(sLine[14:18]) + 1 254 | self.fStepMHZ = int(sLine[19:26]) / 1000.0 #Note it comes in KHZ 255 | self.fRFEGenExpansionPowerDBM = float(sLine[27:32]) 256 | self.bRFEGenPowerON = (sLine[33] == '1') 257 | self.nRFEGenSweepWaitMS = int(sLine[35:40]) 258 | self.m_eMode = RFE_Common.eMode.MODE_GEN_SWEEP_FREQ 259 | elif(sLine[4] == 'G'): 260 | #Normal CW mode 261 | self.fRFEGenCWFreqMHZ = int(sLine[6:13]) / 1000.0 #Note it comes in KHZ 262 | self.fRFEGenExpansionPowerDBM = float(sLine[14:19]) 263 | self.bRFEGenPowerON = (sLine[20] == '1') 264 | self.m_eMode = RFE_Common.eMode.MODE_GEN_CW 265 | else: 266 | self.m_eMode = RFE_Common.eMode.MODE_NONE 267 | bOk = False 268 | elif ((len(sLine) >= 10) and (sLine.startswith("#C4-F:"))): 269 | #Sniffer mode 270 | self.fStartMHZ = int(sLine[6:13]) / 1000.0 #note it comes in KHZ 271 | self.bExpansionBoardActive = (sLine[14] == '1') 272 | self.m_eMode = RFE_Common.eMode(int(sLine[16:19])) 273 | nDelay = int(sLine[20:25]) 274 | self.nBaudrate = int(round(float(RFE_Common.CONST_FCY_CLOCK) / nDelay)) #FCY_CLOCK = 16 * 1000 * 1000 275 | self.eModulations = RFE_Common.eModulation(int(sLine[26:27])) 276 | self.fRBWKHZ = int(sLine[28:33]) 277 | self.fThresholdDBM = (float)(-0.5 * float(sLine[34:37])) 278 | else: 279 | bOk = False 280 | except Exception as obEx: 281 | bOk = False 282 | print("Error in RFEConfiguration - ProcessReceivedString(): " + print(obEx)) 283 | 284 | return bOk 285 | -------------------------------------------------------------------------------- /RFExplorer/RFESweepData.py: -------------------------------------------------------------------------------- 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, duplicate-code 5 | 6 | #============================================================================ 7 | #RF Explorer Python Libraries - A Spectrum Analyzer for everyone! 8 | #Copyright © 2010-21 RF Explorer Technologies SL, 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 | import math 26 | from datetime import datetime 27 | 28 | from RFExplorer import RFE_Common 29 | from RFExplorer import RFExplorer 30 | 31 | class RFESweepData: 32 | """Class support a full sweep of data from RF Explorer, and it is used in the RFESweepDataCollection container 33 | """ 34 | def __init__(self, fStartFreqMHZ, fStepFreqMHZ, nTotalDataPoints): 35 | self.m_Time = datetime.now() 36 | self.m_nTotalDataPoints = nTotalDataPoints 37 | self.m_fStartFrequencyMHZ = fStartFreqMHZ 38 | self.m_fStepFrequencyMHZ = fStepFreqMHZ 39 | self.m_arrAmplitude = [RFE_Common.CONST_MIN_AMPLITUDE_DBM] * self.m_nTotalDataPoints # The actual data container, a consecutive set of dBm amplitude values 40 | self.m_arrBLOB = [] 41 | self.m_sBLOBString = "" #variable used to internall store byte array in string format received if is used externally 42 | 43 | @property 44 | def StartFrequencyMHZ(self): 45 | """Get Start frequency 46 | """ 47 | return self.m_fStartFrequencyMHZ 48 | 49 | @property 50 | def EndFrequencyMHZ(self): 51 | """Get End frequency 52 | """ 53 | return self.GetFrequencyMHZ(self.m_nTotalDataPoints - 1) 54 | 55 | @property 56 | def StepFrequencyMHZ(self): 57 | """Step frequency between each sweep step 58 | """ 59 | return self.m_fStepFrequencyMHZ 60 | @StepFrequencyMHZ.setter 61 | def StepFrequencyMHZ(self, value): 62 | self.m_fStepFrequencyMHZ = value 63 | 64 | @property 65 | def TotalSteps(self): 66 | """Total number of sweep steps captured 67 | """ 68 | return (self.m_nTotalDataPoints - 1) 69 | 70 | @property 71 | def TotalDataPoints(self): 72 | """Total number of sweep data points captured (same as TotalSteps + 1) 73 | """ 74 | return self.m_nTotalDataPoints 75 | 76 | @property 77 | def CaptureTime(self): 78 | """The time when this data sweep was created, it should match as much as 79 | possible the real data capture 80 | """ 81 | return self.m_Time 82 | @CaptureTime.setter 83 | def CaptureTime(self, value): 84 | self.m_Time = value 85 | 86 | def ProcessReceivedString(self, sLine, fOffsetDB, bBLOB=False, bString=False): 87 | """This function will process a received, full consistent string received from remote device 88 | and fill it in all data 89 | 90 | Parameters: 91 | sLine -- String received from device, previously parsed and validated 92 | fOffsetDB -- Currently specified offset in DB 93 | bBLOB -- If true the internal BLOB object will be filled in for later use in GetBLOB 94 | bString -- If true the internal string object will be filled in for later use in GetBLOBString 95 | Returns: 96 | Boolean True if parsing was ok, False otherwise 97 | """ 98 | bOk = True 99 | 100 | try: 101 | if ((len(sLine) > 2) and (sLine[:2] == "$S") and (len(sLine[2:]) == self.m_nTotalDataPoints)): 102 | if (bBLOB): 103 | self.m_arrBLOB = [bytes(0)] * self.m_nTotalDataPoints 104 | objSweep = RFESweepData(self.StartFrequencyMHZ, self.StepFrequencyMHZ, self.m_nTotalDataPoints) 105 | objSweep.CaptureTime = datetime.now() 106 | #print("sLine length: " + str(len(sLine)) +" - "+ "TotalDataPoints: " + str(self.m_nTotalDataPoints)) 107 | if (bString): 108 | self.m_sBLOBString = sLine[2:(self.m_nTotalDataPoints + 2)] 109 | print("sLine: " + sLine[2:(self.m_nTotalDataPoints + 2)]) 110 | 111 | for nInd in range(self.m_nTotalDataPoints): 112 | #print("sLine byte[" + str(nInd) + "]:"+ str(sLine[2 + nInd].encode('utf-8'))) 113 | nVal = ord(sLine[2 + nInd]) 114 | fVal = nVal / -2.0 115 | if (bBLOB): 116 | self.m_arrBLOB[nInd] = nVal 117 | self.SetAmplitudeDBM(nInd, fVal + fOffsetDB) 118 | else: 119 | bOk = False 120 | except Exception as obEx: 121 | print("Error in RFESweepData - ProcessReceivedString(): " + str(obEx)) 122 | bOk = False 123 | 124 | return bOk 125 | 126 | def GetAmplitude_DBM(self, nDataPoint): 127 | """Returns amplitude data in dBm. This is the value as it was read from 128 | the device or from a file so it is not adjusted by offset or additionally compensated in any way. 129 | If the value was read from a device, it may already be an adjusted value including device configured offset. 130 | 131 | Parameters: 132 | nDataPoint -- Internal frequency step or bucket to read data from 133 | Returns: 134 | Float Value in dBm 135 | """ 136 | return self.GetAmplitudeDBM(nDataPoint, None, False) 137 | 138 | def GetAmplitudeDBM(self, nDataPoint, AmplitudeCorrection, bUseCorrection): 139 | """Returns amplitude data in dBm. This is the value as it was read from 140 | the device or from a file so it is not adjusted by offset or additionally compensated in any way. 141 | If the value was read from a device, 142 | it may already be an adjusted value including device configured offset. 143 | 144 | Parameters: 145 | nDataPoint -- Internal frequency data point to read data from 146 | AmplitudeCorrection -- Optional parameter, can be None. If different than None, use the amplitude correction table 147 | bUseCorrection -- If the AmplitudeCorrection is not None, this boolean will tell whether to use it or not 148 | Returns: 149 | Float Value in dBm 150 | """ 151 | if (nDataPoint < self.m_nTotalDataPoints): 152 | if ((AmplitudeCorrection) and bUseCorrection): 153 | return self.m_arrAmplitude[nDataPoint] + AmplitudeCorrection.GetAmplitudeCalibration(int(self.GetFrequencyMHZ(nDataPoint))) 154 | else: 155 | return self.m_arrAmplitude[nDataPoint] 156 | else: 157 | return RFE_Common.CONST_MIN_AMPLITUDE_DBM 158 | 159 | def SetAmplitudeDBM(self, nDataPoint, fDBM): 160 | """Set Amplitude in dBm 161 | 162 | Parameters: 163 | nDataPoint -- Internal frequency data point where to set data 164 | fDBM -- New value in dBm 165 | """ 166 | if (nDataPoint < self.m_nTotalDataPoints): 167 | self.m_arrAmplitude[nDataPoint] = fDBM 168 | 169 | def GetFrequencyMHZ(self, nDataPoint): 170 | """Get frequency in MHz 171 | 172 | Parameters: 173 | nDataPoint -- Internal frequency data point to read data from 174 | Returns: 175 | Float Frequency in MHz, zero otherwise 176 | """ 177 | if (nDataPoint < self.m_nTotalDataPoints): 178 | return self.m_fStartFrequencyMHZ + (self.m_fStepFrequencyMHZ * nDataPoint) 179 | else: 180 | return 0.0 181 | 182 | def GetFrequencySpanMHZ(self): 183 | """Get frequency span in MHz 184 | 185 | Returns: 186 | Float Frequency span in MHz 187 | """ 188 | return (self.m_fStepFrequencyMHZ * self.TotalSteps) 189 | 190 | def GetMinDataPoint(self): 191 | """Returns the step of the lowest amplitude value found 192 | 193 | Returns: 194 | Integer The data point of the lowest amplitude value 195 | """ 196 | nMinDataPoint = 0 197 | fMin = RFE_Common.CONST_MAX_AMPLITUDE_DBM 198 | for nInd in range(self.m_nTotalDataPoints): 199 | if (fMin > self.m_arrAmplitude[nInd]): 200 | fMin = self.m_arrAmplitude[nInd] 201 | nMinDataPoint = nInd 202 | return nMinDataPoint 203 | 204 | def GetPeakDataPoint(self): 205 | """Returns the step of the highest amplitude value found 206 | 207 | Returns: 208 | Integer The data point of the highest amplitude value 209 | """ 210 | nPeakDataPoint = 0 211 | fPeak = RFE_Common.CONST_MIN_AMPLITUDE_DBM 212 | 213 | for nInd in range(self.m_nTotalDataPoints): 214 | if (fPeak < self.m_arrAmplitude[nInd]): 215 | fPeak = self.m_arrAmplitude[nInd] 216 | nPeakDataPoint = nInd 217 | return nPeakDataPoint 218 | 219 | def IsSameConfiguration(self, objOther): 220 | """Compare new object configuration with stored configuration data 221 | 222 | Parameters: 223 | objOther -- New configuration object 224 | Returns: 225 | Boolean True if they are the same, False otherwise 226 | """ 227 | return (math.fabs(objOther.StartFrequencyMHZ - self.StartFrequencyMHZ) < 0.001 and math.fabs(objOther.StepFrequencyMHZ - self.StepFrequencyMHZ) < 0.001 and (objOther.TotalSteps == self.TotalSteps)) 228 | 229 | def Duplicate(self): 230 | """Duplicate RFESweepData object 231 | 232 | Returns: 233 | RFESweepData Duplicate object 234 | """ 235 | objSweep = RFESweepData(self.StartFrequencyMHZ, self.StepFrequencyMHZ, self.m_nTotalDataPoints) 236 | 237 | objSweep.m_arrAmplitude = self.m_arrAmplitude.copy() 238 | 239 | return objSweep 240 | 241 | def GetChannelPowerDBM(self): 242 | """Returns power channel over the full span being captured. The power is instantaneous real time 243 | For average power channel use the collection method GetAverageChannelPower(). 244 | 245 | Returns: 246 | Float Channel power in dBm/span 247 | """ 248 | fChannelPower = RFE_Common.CONST_MIN_AMPLITUDE_DBM 249 | fPowerTemp = 0.0 250 | 251 | for nInd in range(self.m_nTotalDataPoints): 252 | fPowerTemp += RFExplorer.Convert_dBm_2_mW(self.m_arrAmplitude[nInd]) 253 | 254 | if (fPowerTemp > 0.0): 255 | #add here actual RBW calculation in the future - currently we are 256 | #assuming frequency step is the same 257 | #as RBW which is not 100% accurate. 258 | fChannelPower = RFExplorer.Convert_mW_2_dBm(fPowerTemp) 259 | 260 | return fChannelPower 261 | 262 | def Dump(self): 263 | """ Dump a CSV string line with sweep data. 264 | 265 | Returns: 266 | String All sweep data in CSV format 267 | """ 268 | 269 | sResult = "Sweep data: " + str("{0:.3f}".format(self.StartFrequencyMHZ)) + " - " + "MHz " + str("{0:.3f}".format(self.StepFrequencyMHZ)) + "MHz " + " - " + "Steps: " + str(self.TotalSteps) 270 | try: 271 | for nDataPoint in range(self.m_nTotalDataPoints): 272 | if (nDataPoint > 0): 273 | sResult += "," 274 | if ((nDataPoint % 16) == 0): 275 | sResult += "\n" 276 | sResult += str('{:04.1f}'.format(self.GetAmplitudeDBM(nDataPoint, None, False))) 277 | except Exception as obEx: 278 | if self.m_nVerboseLevel>0: 279 | print("m_arrReceivedStrings processing: " + str(obEx)) 280 | 281 | return sResult 282 | 283 | def SaveFileCSV(self, sFilename, cCSVDelimiter, AmplitudeCorrection): 284 | """Save a CSV file using one frequency point/dBm value per line 285 | 286 | Parameters: 287 | sFilename -- Full path filename 288 | cCSVDelimiter -- Comma delimiter to use 289 | AmplitudeCorrection -- Optional parameter, can be None. If different than None, use the amplitude correction table 290 | """ 291 | try: 292 | with open(sFilename, 'w') as objWriter: 293 | for nStep in range(self.TotalDataPoints): 294 | objWriter.write("{0:.3f}".format(self.GetFrequencyMHZ(nStep))) 295 | objWriter.write(cCSVDelimiter) 296 | objWriter.write("{0:.1f}".format(self.GetAmplitudeDBM(nStep, AmplitudeCorrection, AmplitudeCorrection != None))) 297 | objWriter.write("\n") 298 | except Exception as obEx: 299 | print("Error: " + str(obEx)) 300 | -------------------------------------------------------------------------------- /RFExplorer/RFESweepDataCollection.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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 | from RFExplorer import RFE_Common 26 | from RFExplorer.RFESweepData import RFESweepData 27 | 28 | class RFESweepDataCollection: 29 | """ Allocates up to nCollectionSize elements to start with the container. 30 | """ 31 | def __init__(self, nCollectionSize, bAutogrow): 32 | self.m_arrData = [] #Collection of available spectrum data items 33 | self.m_MaxHoldData = None #Single data set, defined for the whole collection and updated with Add, to 34 | #keep the Max Hold values 35 | self.m_nUpperBound = -1 #Max value for index with available data 36 | self.m_nInitialCollectionSize = 0 37 | 38 | if (nCollectionSize > RFE_Common.CONST_MAX_ELEMENTS): 39 | nCollectionSize = RFE_Common.CONST_MAX_ELEMENTS 40 | 41 | self.m_bAutogrow = bAutogrow #true if the array bounds may grow up to _MAX_ELEMENTS, otherwise will 42 | #be limited to initial collection size 43 | 44 | self.m_nInitialCollectionSize = nCollectionSize 45 | 46 | self.CleanAll() 47 | 48 | @property 49 | def MaxHoldData(self): 50 | """Single data set, defined for the whole collection and updated with Add, to keep the Max Hold values 51 | """ 52 | return self.m_MaxHoldData 53 | 54 | @property 55 | def Count(self): 56 | """ Returns the total of elements with actual data allocated. 57 | """ 58 | return int(self.m_nUpperBound + 1) 59 | 60 | @property 61 | def UpperBound(self): 62 | """ Returns the highest valid index of elements with actual data allocated. 63 | """ 64 | return self.m_nUpperBound 65 | 66 | @classmethod 67 | def FileHeaderVersioned_001(cls): 68 | """File format constant - 001 69 | 70 | Returns: 71 | String 001 File header version 72 | """ 73 | return "RFExplorer PC Client - Format v001" 74 | 75 | @classmethod 76 | def FileHeaderVersioned(cls): 77 | """File format constant indicates the latest known and supported file format 78 | 79 | Returns: 80 | String File header version 81 | """ 82 | return "RFExplorer PC Client - Format v" + "{:03d}".format(RFE_Common.CONST_FILE_VERSION) 83 | 84 | def GetData(self, nIndex): 85 | """ Return the data pointed by the zero-starting index 86 | 87 | Parameters: 88 | nIndex -- Index to find specific data inside the data array 89 | Returns: 90 | RFESweepData None if no data is available with this index 91 | """ 92 | if (nIndex <= self.m_nUpperBound): 93 | return self.m_arrData[nIndex] 94 | else: 95 | return None 96 | 97 | def IsFull(self): 98 | """ True when the absolute maximum of allowed elements in the container is allocated 99 | 100 | Returns: 101 | Boolean True when the absolute maximum of allowed elements in the container is allocated, False otherwise 102 | """ 103 | return (self.m_nUpperBound >= RFE_Common.CONST_MAX_ELEMENTS) 104 | 105 | def Add(self, SweepData): 106 | """This function add a single sweep data in the collection 107 | 108 | Parameters: 109 | SweepData -- A single sweep data 110 | Returns: 111 | Boolean True it sweep data is added, False otherwise 112 | """ 113 | try: 114 | if (self.IsFull()): 115 | return False 116 | 117 | if (not self.m_MaxHoldData): 118 | self.m_MaxHoldData = RFESweepData(SweepData.StartFrequencyMHZ, SweepData.StepFrequencyMHZ, SweepData.TotalDataPoints) 119 | if (self.m_nUpperBound >= (len(self.m_arrData) - 1)): 120 | if (self.m_bAutogrow): 121 | self.ResizeCollection(10 * 1000) #add 10K samples more 122 | else: 123 | #move all items one position down, lose the older one at position 0 124 | self.m_nUpperBound = len(self.m_arrData) - 2 125 | self.m_arrData[0] = None 126 | for nInd in self.m_nUpperBound: 127 | self.m_arrData[nInd] = self.m_arrData[nInd + 1] 128 | 129 | self.m_nUpperBound += 1 130 | self.m_arrData[self.m_nUpperBound] = SweepData 131 | 132 | nInd = 0 133 | while nInd < SweepData.TotalDataPoints: 134 | if (SweepData.GetAmplitudeDBM(nInd, None, False) > self.m_MaxHoldData.GetAmplitudeDBM(nInd, None, False)): 135 | self.m_MaxHoldData.SetAmplitudeDBM(nInd, SweepData.GetAmplitudeDBM(nInd, None, False)) 136 | nInd += 1 137 | except Exception as obEx: 138 | print("Error in RFESweepDataCollection - Add(): " + str(obEx)) 139 | return False 140 | 141 | return True 142 | 143 | def CleanAll(self): 144 | """Initialize internal data 145 | """ 146 | self.m_arrData = [RFESweepData] * self.m_nInitialCollectionSize 147 | self.m_MaxHoldData = None 148 | self.m_nUpperBound = -1 149 | 150 | def Dump(self): 151 | """Dump a CSV string line with sweep data collection 152 | 153 | Returns: 154 | String All sweep data collection 155 | """ 156 | sDump = "" 157 | for nIndex in range(self.Count): 158 | objSweep = self.m_arrData[nIndex] 159 | if (sDump != ""): 160 | sDump += '\n' 161 | if (objSweep): 162 | sDump += objSweep.Dump() 163 | else: 164 | sDump += "Sweep {null}" 165 | return sDump 166 | 167 | def GetMedianAverage(self, nStart, nEnd): 168 | """Return a SweepData object with median average data 169 | 170 | Parameters: 171 | nStart -- Start frequency for the median average calculation 172 | nEnd -- End frequency for the median average calculation 173 | Returns: 174 | RFESweepData object with median average data, None otherwise 175 | """ 176 | #string sDebugText = "" 177 | if (nStart > self.m_nUpperBound or nEnd > self.m_nUpperBound or nStart > nEnd): 178 | return None 179 | 180 | nTotalIterations = nEnd - nStart + 1 181 | try: 182 | objReturn = RFESweepData(self.m_arrData[nEnd].StartFrequencyMHZ, self.m_arrData[nEnd].StepFrequencyMHZ, self.m_arrData[nEnd].TotalDataPoints) 183 | 184 | for nSweepInd in objReturn.TotalDataPoints: 185 | #sDebugText += "[" + nSweepInd + "]:" 186 | fSweepValue = 0.0 187 | arrSweepValues = [0.0] * nTotalIterations 188 | 189 | nIterationInd = nStart 190 | while(nIterationInd <= nEnd) : 191 | if (nSweepInd == 0): 192 | #check all the sweeps use the same configuration, but 193 | #only in first loop to reduce overhead 194 | if (not self.m_arrData[nIterationInd].IsSameConfiguration(objReturn)): 195 | return None 196 | arrSweepValues[nIterationInd - nStart] = self.m_arrData[nIterationInd].GetAmplitudeDBM(nSweepInd, None, False) 197 | #sDebugText += str(self.m_arrData[nIterationInd].GetAmplitudeDBM(nSweepInd)) + "," 198 | nIterationInd += 1 199 | 200 | arrSweepValues.sort() 201 | fSweepValue = arrSweepValues[nTotalIterations / 2] 202 | #sDebugText += "(" + str(fSweepValue) + ")" 203 | objReturn.SetAmplitudeDBM(nSweepInd, fSweepValue) 204 | except Exception as obEx: 205 | print("Error in RFESweedDataCollection - GetMedianAverage(): " + str(obEx)) 206 | objReturn = None 207 | 208 | return objReturn 209 | 210 | def GetAverage(self, nStart, nEnd): 211 | """Return a SweepData object with average data 212 | 213 | Parameters: 214 | nStart -- Start frequency for the average calculation 215 | nEnd -- End frequency for the average calculation 216 | Returns: 217 | RFESweepData object with average data, None otherwise 218 | """ 219 | #string sDebugText = "" 220 | 221 | if (nStart > self.m_nUpperBound or nEnd > self.m_nUpperBound or nStart > nEnd): 222 | return None 223 | 224 | try: 225 | objReturn = RFESweepData(self.m_arrData[nEnd].StartFrequencyMHZ, self.m_arrData[nEnd].StepFrequencyMHZ, self.m_arrData[nEnd].TotalSteps) 226 | 227 | for nSweepInd in objReturn.TotalSteps: 228 | #sDebugText += "[" + nSweepInd + "]:" 229 | fSweepValue = 0.0 230 | 231 | nIterationInd = nStart 232 | while (nIterationInd <= nEnd): 233 | if (nSweepInd == 0): 234 | #check all the sweeps use the same configuration, but 235 | #only in first loop to reduce overhead 236 | if (not self.m_arrData[nIterationInd].IsSameConfiguration(objReturn)): 237 | return None 238 | 239 | fSweepValue += self.m_arrData[nIterationInd].GetAmplitudeDBM(nSweepInd, None, False) 240 | #sDebugText += 241 | #str(self.m_arrData[nIterationInd].GetAmplitudeDBM(nSweepInd)) 242 | #+ "," 243 | nIterationInd += 1 244 | 245 | fSweepValue = fSweepValue / (nEnd - nStart + 1) 246 | #sDebugText += "(" + str(fSweepValue) + ")" 247 | objReturn.SetAmplitudeDBM(nSweepInd, fSweepValue) 248 | 249 | except Exception as obEx: 250 | objReturn = None 251 | print("Error in RFESweedDataCollection - GetAverage(): " + str(obEx)) 252 | 253 | return objReturn 254 | 255 | def SaveFileCSV(self, sFilename, cCSVDelimiter, AmplitudeCorrection): 256 | """Will write large, complex, multi-sweep CSV file. No save anything, if there are no data 257 | 258 | Parameters: 259 | sFilename -- Full path filename 260 | cCSVDelimiter -- Comma delimiter to use 261 | AmplitudeCorrection -- Optional parameter, can be None. If different than None, use the amplitude correction table 262 | """ 263 | if (self.m_nUpperBound <= 0): 264 | return 265 | 266 | objFirst = self.m_arrData[0] 267 | try: 268 | with open(sFilename, 'w') as objWriter: 269 | objWriter.write("RF Explorer CSV data file: " + self.FileHeaderVersioned() + '\n' + \ 270 | "Start Frequency: " + str(objFirst.StartFrequencyMHZ) + "MHZ" + '\n' + \ 271 | "Step Frequency: " + str(objFirst.StepFrequencyMHZ * 1000) + "KHZ" + '\n' + \ 272 | "Total data entries: " + str(self.m_nUpperBound) + '\n' + \ 273 | "Steps per entry: " + str(objFirst.TotalSteps)+ '\n') 274 | 275 | sHeader = "Sweep" + cCSVDelimiter + "Date" + cCSVDelimiter + "Time" + cCSVDelimiter + "Milliseconds" 276 | 277 | for nStep in range(objFirst.TotalDataPoints): 278 | dFrequency = objFirst.StartFrequencyMHZ + nStep * (objFirst.StepFrequencyMHZ) 279 | sHeader += cCSVDelimiter + "{:08.3f}".format(dFrequency) 280 | 281 | objWriter.write(sHeader + '\n') 282 | 283 | for nSweepInd in range(self.m_nUpperBound): 284 | objWriter.write(str(nSweepInd) + cCSVDelimiter) 285 | 286 | objWriter.write(str(self.m_arrData[nSweepInd].CaptureTime.date()) + cCSVDelimiter + \ 287 | str(self.m_arrData[nSweepInd].CaptureTime.time())[:-7] + cCSVDelimiter + \ 288 | '.' +'{:03}'.format(int(str(getattr(self.m_arrData[nSweepInd].CaptureTime.time(), 'microsecond'))[:-3])) + cCSVDelimiter) 289 | 290 | if (not self.m_arrData[nSweepInd].IsSameConfiguration(objFirst)): 291 | break 292 | 293 | for nStep in range(objFirst.TotalSteps): 294 | objWriter.write(str(self.m_arrData[nSweepInd].GetAmplitudeDBM(nStep, AmplitudeCorrection, (AmplitudeCorrection != None)))) 295 | if (nStep != (objFirst.TotalSteps - 1)): 296 | objWriter.write(cCSVDelimiter) 297 | 298 | objWriter.write('\n') 299 | 300 | except Exception as objEx: 301 | print("Error in RFESweepDataCollection - SaveFileCSV(): " + str(objEx)) 302 | 303 | def GetTopBottomDataRange(self, dTopRangeDBM, dBottomRangeDBM, AmplitudeCorrection): 304 | """Return estimated Top and Bottom level using data collection, no return anything if sweep data collection is empty 305 | 306 | Parameters: 307 | dTopRangeDBM -- Bottom level in dBm 308 | dBottomRangeDBM -- Top level in dBm 309 | AmplitudeCorrection -- Optional parameter, can be None. If different than None, use the amplitude correction table 310 | Returns: 311 | Float Bottom level in dBm 312 | Float Top level in dBm 313 | """ 314 | dTopRangeDBM = RFE_Common.CONST_MIN_AMPLITUDE_DBM 315 | dBottomRangeDBM = RFE_Common.CONST_MAX_AMPLITUDE_DBM 316 | 317 | if (self.m_nUpperBound <= 0): 318 | return 319 | 320 | for nIndSample in self.m_nUpperBound: 321 | for nIndStep in self.m_arrData[0].TotalSteps: 322 | dValueDBM = self.m_arrData[nIndSample].GetAmplitudeDBM(nIndStep, AmplitudeCorrection, (AmplitudeCorrection != None)) 323 | if (dTopRangeDBM < dValueDBM): 324 | dTopRangeDBM = dValueDBM 325 | if (dBottomRangeDBM > dValueDBM): 326 | dBottomRangeDBM = dValueDBM 327 | 328 | return dTopRangeDBM, dBottomRangeDBM 329 | 330 | def ResizeCollection(self, nSizeToAdd): 331 | """Change data collection size 332 | 333 | Parameters: 334 | nSizeToAdd -- Number of sample to add to the sweep data collection 335 | """ 336 | self.m_arrData += [RFESweepData] * nSizeToAdd 337 | -------------------------------------------------------------------------------- /RFExplorer/RFE_Common.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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 | from enum import Enum 26 | 27 | #Firmware version of RF Explorer device which was tested and certified with this library version 28 | CONST_RFESA_FIRMWARE_CERTIFIED = "01.33"; #Standard Analyzer 29 | CONST_RFESA_PLUS_FIRMWARE_CERTIFIED = "03.28"; #Plus Analizer models 30 | CONST_RFEGEN_FIRMWARE_CERTIFIED = "01.34"; #Generator 31 | CONST_RFESA_AUDIOPRO_FIRMWARE_CERTIFIED = "03.08"; #RF Explorer AudioPro model 32 | CONST_RFESA_IOT_FIRMWARE_CERTIFIED = "01.15"; #IoT module 33 | 34 | CONST_FCY_CLOCK = 16 * 1000 * 1000 #RFECommunicator - public const UInt32 FCY_CLOCK = 16 * 1000 * 1000 35 | CONST_MIN_AMPLITUDE_DBM = -120.0 #RFECommunicator - public const float MIN_AMPLITUDE_DBM = -120.0f 36 | CONST_MAX_SPECTRUM_STEPS = 65535 #RFECommunicator - public const UInt16 MAX_SPECTRUM_STEPS = 65535 37 | CONST_MAX_AMPLITUDE_DBM = 50.0 #RFECommunicator - public const float MAX_AMPLITUDE_DBM = 50.0f 38 | CONST_MAX_ELEMENTS = (1000) #This is the absolute max size that can be allocated 39 | CONST_FILE_VERSION = 2 #File format constant indicates the latest known and supported file format 40 | CONST_ACKNOWLEDGE = "#ACK" 41 | 42 | CONST_RFGEN_MIN_FREQ_MHZ = 23.438 43 | CONST_RFGEN_MAX_FREQ_MHZ = 6000 44 | CONST_RFGENEXP_MIN_FREQ_MHZ = 0.100 45 | CONST_RFGENEXP_MAX_FREQ_MHZ = 6000 46 | 47 | CONST_RFE_MIN_SWEEP_POINTS = 112; 48 | CONST_RFE_MIN_SWEEP_STEPS = CONST_RFE_MIN_SWEEP_POINTS - 1; 49 | 50 | CONST_RESETSTRING = "(C) Ariel Rocholl " 51 | 52 | CONST_EEOT = "\xFF\xFE\xFF\xFE\x00" #this indicates Early End Of Transmission, sent by devices with firmware > 1.27 and 3.10 53 | 54 | CONST_POS_INTERNAL_CALIBRATED_6G = 134 #start position for 6G model 55 | CONST_POS_INTERNAL_CALIBRATED_MWSUB3G = 0 #start position for MWSUB3G model 56 | CONST_POS_INTERNAL_CALIBRATED_WSUB1G = 15 #start position for WSUB1G model 57 | CONST_POS_END_INTERNAL_CALIBRATED_WSUB1G = 58 #end position for WSUB1G, that is, POS_INTERNAL_CALIBRATED_WSUB1G = 15 + WSUB1G calibration data size = 43 58 | CONST_POS_INTERNAL_CALIBRATED_WSUB1G_PLUS = 0 #start position for WSUB1G+ model 59 | CONST_POS_END_INTERNAL_CALIBRATED_WSUB1G_PLUS = 258 #end position for WSUB1G+, that is, POS_INTERNAL_CALIBRATED_WSUB1G_PLUS = 0 + WSUB1G calibration data size = 258 ((R1+R2+R3)86 x 3) 60 | 61 | #--------------------------------------------------------- 62 | 63 | class eModel(Enum): 64 | """All possible RF Explorer model values 65 | """ 66 | MODEL_433 = 0 67 | MODEL_868 = 1 68 | MODEL_915 = 2 69 | MODEL_WSUB1G = 3 70 | MODEL_2400 = 4 71 | MODEL_WSUB3G = 5 72 | MODEL_6G = 6 73 | 74 | MODEL_WSUB1G_PLUS = 10 75 | MODEL_AUDIOPRO = 11 #note this is converted internally to MODEL_WSUB3G to simplify code, but sets m_bAudioPro to true 76 | MODEL_2400_PLUS = 12 77 | MODEL_4G_PLUS = 13 78 | MODEL_6G_PLUS = 14 79 | 80 | MODEL_W5G3G = 16 81 | MODEL_W5G4G = 17 82 | MODEL_W5G5G = 18 83 | 84 | MODEL_RFGEN = 60 85 | MODEL_RFGEN_EXPANSION = 61 86 | 87 | MODEL_NONE = 0xFF 88 | 89 | class eMode(Enum): 90 | """The current operational mode 91 | """ 92 | MODE_SPECTRUM_ANALYZER = 0 93 | MODE_TRANSMITTER = 1 94 | MODE_WIFI_ANALYZER = 2 95 | MODE_TRACKING = 5 96 | MODE_SNIFFER = 6 97 | 98 | MODE_GEN_CW = 60 99 | MODE_GEN_SWEEP_FREQ = 61 100 | MODE_GEN_SWEEP_AMP = 62 101 | 102 | MODE_NONE = 0xFF 103 | 104 | class eCalculator(Enum): 105 | """The current configured calculator in the device 106 | """ 107 | NORMAL = 0 108 | MAX = 1 109 | AVG = 2 110 | OVERWRITE = 3 111 | MAX_HOLD = 4 112 | MAX_HISTORICAL = 5 113 | UNKNOWN = 0xff 114 | 115 | class eModulation(Enum): 116 | """Modulation being used 117 | """ 118 | MODULATION_OOK_RAW = 0 119 | MODULATION_PSK_RAW = 1 120 | MODULATION_OOK_STD = 2 121 | MODULATION_PSK_STD = 3 122 | MODULATION_NONE = 0xFF 123 | 124 | class eDSP(Enum): 125 | """All possible DSP values 126 | """ 127 | DSP_AUTO = 0 128 | DSP_FILTER = 1 129 | DSP_FAST = 2 130 | DSP_NO_IMG = 3 131 | 132 | class eInputStage(Enum): 133 | """Input stages modes 134 | """ 135 | Direct = 0 136 | Attenuator_30dB = 1 137 | LNA_25dB = 2 138 | Attenuator_60dB = 3 139 | LNA_12dB = 4 140 | 141 | 142 | 143 | #--------------------------------------------------------- 144 | -------------------------------------------------------------------------------- /RFExplorer/ReceiveSerialThread.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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 | import threading 26 | import time 27 | 28 | from RFExplorer import RFE_Common 29 | from RFExplorer.RFEConfiguration import RFEConfiguration 30 | from RFExplorer.RFESweepData import RFESweepData 31 | 32 | class ReceiveSerialThread(threading.Thread): 33 | """The secondary thread used to get data from USB/RS232 COM port 34 | """ 35 | def __init__(self,objRFECommunicator, objQueue, objSerialPort, hQueueLock, hSerialPortLock): 36 | threading.Thread.__init__(self) 37 | self.variable = RFE_Common.CONST_MAX_AMPLITUDE_DBM 38 | self.m_objRFECommunicator = objRFECommunicator 39 | self.m_objQueue = objQueue 40 | self.m_objSerialPort = objSerialPort 41 | self.m_hQueueLock = hQueueLock 42 | self.m_hSerialPortLock = hSerialPortLock 43 | self.m_objCurrentConfiguration = None 44 | 45 | def run(self): 46 | #print("Starting Thread") 47 | self.ReceiveThreadfunc() 48 | #print("Exiting Thread") 49 | 50 | def __del__(self): 51 | pass 52 | #print("destroying thread object") 53 | 54 | def ReceiveThreadfunc(self): 55 | """Where all data coming from the device are processed and queued 56 | """ 57 | nBytes = 0 58 | nTotalSpectrumDataDumps = 0 59 | while self.m_objRFECommunicator.RunReceiveThread: 60 | strReceived = "" 61 | while (self.m_objRFECommunicator.PortConnected and self.m_objRFECommunicator.RunReceiveThread): 62 | sNewText = "" 63 | self.m_hSerialPortLock.acquire() 64 | try: 65 | if (self.m_objSerialPort.is_open): 66 | #print("port open") 67 | nBytes = self.m_objSerialPort.in_waiting 68 | if (nBytes > 0): 69 | sNewText = self.m_objSerialPort.read(nBytes).decode("latin_1") 70 | except Exception as obEx: 71 | print("Serial port Exception: " + str(obEx)) 72 | finally: 73 | self.m_hSerialPortLock.release() 74 | if (len(sNewText) > 0): 75 | strReceived += str(sNewText) 76 | if(self.m_objRFECommunicator.VerboseLevel > 9): 77 | print(strReceived.encode('utf-8')) 78 | sNewText = "" 79 | if (len(strReceived) > 66*1024): 80 | #Safety code, some error prevented the string from being processed in several loop cycles.Reset it. 81 | if(self.m_objRFECommunicator.VerboseLevel > 5): 82 | print("Received string truncated (" + strReceived + ")") 83 | strReceived = "" 84 | nLen = len(strReceived) 85 | if (nLen > 1): 86 | if (self.m_objRFECommunicator.VerboseLevel > 9): 87 | print(strReceived) 88 | if (strReceived[0] == '#'): 89 | nEndPos = strReceived.find("\r\n") 90 | if (nEndPos >= 0): 91 | sNewLine = strReceived[:nEndPos] 92 | sLeftOver = strReceived[nEndPos + 2:] 93 | strReceived = sLeftOver 94 | #print(sNewLine) 95 | 96 | if ((len(sNewLine) > 5) and ((sNewLine[:6] == "#C2-F:") or sNewLine.startswith("#C2-f:") or (sNewLine[:4] == "#C3-") and (sNewLine[4] != 'M') or sNewLine.startswith("#C4-F:")) or sNewLine.startswith("#C5-")): 97 | if (self.m_objRFECommunicator.VerboseLevel > 5): 98 | print("Received Config:" + sNewLine) 99 | 100 | #Standard configuration expected 101 | objNewConfiguration = RFEConfiguration(None) 102 | #print("sNewLine: "+ sNewLine) 103 | if (objNewConfiguration.ProcessReceivedString(sNewLine)): 104 | self.m_objCurrentConfiguration = RFEConfiguration(objNewConfiguration) 105 | self.m_hQueueLock.acquire() 106 | self.m_objQueue.put(objNewConfiguration) 107 | self.m_hQueueLock.release() 108 | else: 109 | self.m_hQueueLock.acquire() 110 | self.m_objQueue.put(sNewLine) 111 | self.m_hQueueLock.release() 112 | elif (strReceived[0] == '$'): 113 | if (nLen > 4 and (strReceived[1] == 'C')): 114 | nSize = 2 #account for cr+lf 115 | #calibration data 116 | if (strReceived[2] == 'c') or (strReceived[2] == 'd'): 117 | nSize+=int(ord(strReceived[3])) + 4 118 | elif (strReceived[2] == 'b'): 119 | nSize+=(int(ord(strReceived[4])) + 1) * 16 + 10 120 | if (nSize > 2 and nLen >= nSize): 121 | nEndPos = strReceived.find("\r\n") 122 | sNewLine = strReceived[:nEndPos] 123 | sLeftOver = strReceived[nEndPos:] 124 | strReceived = sLeftOver 125 | #print(" [" + " ".join("{:02X}".format(ord(c)) for 126 | #c in sNewLine) + "]") 127 | self.m_hQueueLock.acquire() 128 | self.m_objQueue.put(sNewLine) 129 | self.m_hQueueLock.release() 130 | elif (nLen > 2 and ((strReceived[1] == 'q') or (strReceived[1] == 'Q'))): 131 | #this is internal calibration data dump 132 | nReceivedLength = int(ord(strReceived[2])) 133 | nExtraLength = 3 134 | if (strReceived[1] == 'Q'): 135 | nReceivedLength += int(0x100 * strReceived[3]) 136 | nExtraLength = 4 137 | 138 | bLengthOK = (len(strReceived) >= (nExtraLength + nReceivedLength + 2)) 139 | if (bLengthOK): 140 | self.m_hQueueLock.acquire() 141 | self.m_objQueue.put(strReceived) 142 | self.m_hQueueLock.release() 143 | strReceived = strReceived[(nExtraLength + nReceivedLength + 2):] 144 | elif (nLen > 1 and (strReceived[1] == 'D')): 145 | #This is dump screen data 146 | if (self.m_objRFECommunicator.VerboseLevel > 5): 147 | print("Received $D" + len(strReceived)) 148 | 149 | if (len(strReceived) >= (4 + 128 * 8)): 150 | pass 151 | elif (nLen > 3 and ((strReceived[1] == "S") or (strReceived[1] == 's') or (strReceived[1] == 'z'))): 152 | #Standard spectrum analyzer data 153 | nReceivedLength = int(ord(strReceived[2])) 154 | nSizeChars = 3 155 | if (strReceived[1] == 's'): 156 | if (nReceivedLength == 0): 157 | nReceivedLength = 256 158 | nReceivedLength *= 16 159 | elif (strReceived[1] == 'z'): 160 | nReceivedLength *= 256 161 | nReceivedLength += int(ord(strReceived[3])) 162 | nSizeChars+=1 163 | if (self.m_objRFECommunicator.VerboseLevel > 9): 164 | print("Spectrum data: " + str(nReceivedLength) + " " + str(nLen)) 165 | bLengthOK = (nLen >= (nSizeChars + nReceivedLength + 2)) #OK if received data >= header command($S,$s or $z) + data length + end of line('\n\r') 166 | bFullStringOK = False 167 | if (bLengthOK): ## Ok if data length are ok and end of line('\r\n') is in the correct place 168 | ## (at the end). Prevents corrupted data 169 | bFullStringOK = (ord(strReceived[nSizeChars + nReceivedLength:][0]) == ord('\r')) and (ord(strReceived[nSizeChars + nReceivedLength:][1]) == ord('\n')) 170 | if (self.m_objRFECommunicator.VerboseLevel > 9): 171 | print("bLengthOK " + str(bLengthOK) + "bFullStringOK " + str(bFullStringOK) + " " + str(ord(strReceived[nSizeChars + nReceivedLength:][1])) + " - " + strReceived[nSizeChars + nReceivedLength:]) 172 | 173 | bEEOT = False 174 | if (bLengthOK==False or ((bLengthOK==True) and (bFullStringOK == False))): 175 | #Check if not all bytes were received but EEOT is detected. 176 | nIndexEEOT = strReceived.find(RFE_Common.CONST_EEOT) 177 | if (nIndexEEOT != -1): 178 | bEEOT = True 179 | if (self.m_objRFECommunicator.VerboseLevel > 9): 180 | print("EEOT detected") 181 | #If EEOT detected, remove from received string so we ignore the partially received data 182 | strReceived = strReceived[(nIndexEEOT + len(RFE_Common.CONST_EEOT)):] 183 | 184 | if (bFullStringOK): 185 | nTotalSpectrumDataDumps+=1 186 | if (self.m_objRFECommunicator.VerboseLevel > 9): 187 | print("Full dump received: " + str(nTotalSpectrumDataDumps)) 188 | 189 | #So we are here because received the full set of chars expected, and all them are apparently of valid characters 190 | if (nReceivedLength <= RFE_Common.CONST_MAX_SPECTRUM_STEPS): 191 | sNewLine = "$S" + strReceived[nSizeChars:(nSizeChars + nReceivedLength)] 192 | if (self.m_objRFECommunicator.VerboseLevel > 9): 193 | print("New line:\n" + " [" + "".join("{:02X}".format(ord(c)) for c in sNewLine) + "]") 194 | if (self.m_objCurrentConfiguration): 195 | nSweepDataPoints = self.m_objCurrentConfiguration.FreqSpectrumSteps + 1 196 | objSweep = RFESweepData(self.m_objCurrentConfiguration.fStartMHZ, self.m_objCurrentConfiguration.fStepMHZ, nSweepDataPoints) 197 | nInputStageOffset = 0 198 | #IoT module calculate this offset internally, this avoid add offset twice if is IoT (MWSUB3G), same as 2.4G+ 199 | if ((self.m_objRFECommunicator.InputStage != RFE_Common.eInputStage.Direct) and self.m_objRFECommunicator.IsAnalyzerEmbeddedCal() and (self.m_objRFECommunicator.IsMWSUB3G == False) and (self.m_objRFECommunicator.ActiveModel != RFE_Common.eModel.MODEL_2400_PLUS)): 200 | nInputStageOffset = int(self.m_objRFECommunicator.InputStageAttenuationDB) 201 | if (objSweep.ProcessReceivedString(sNewLine, (self.m_objCurrentConfiguration.fOffset_dB + nInputStageOffset), self.m_objRFECommunicator.UseByteBLOB, self.m_objRFECommunicator.UseStringBLOB)): 202 | if (self.m_objRFECommunicator.VerboseLevel > 5): 203 | print(objSweep.Dump()) 204 | if (nSweepDataPoints > 5): #check this is not an incomplete scan (perhaps from a stopped SNA tracking step) 205 | #Normal spectrum analyzer sweep data 206 | self.m_hQueueLock.acquire() 207 | self.m_objQueue.put(objSweep) 208 | self.m_hQueueLock.release() 209 | elif (self.m_objRFECommunicator.VerboseLevel > 5): 210 | self.m_hQueueLock.acquire() 211 | self.m_objQueue.put(sNewLine) 212 | self.m_hQueueLock.release() 213 | else: 214 | if (self.m_objRFECommunicator.VerboseLevel > 5): 215 | print("Configuration not available yet. $S string ignored.") 216 | else: 217 | self.m_hQueueLock.acquire() 218 | self.m_objQueue.put("Ignored $S of size " + str(nReceivedLength) + " expected " + str(self.m_objCurrentConfiguration.FreqSpectrumSteps)) 219 | self.m_hQueueLock.release() 220 | strReceived = strReceived[(nSizeChars + nReceivedLength + 2):] 221 | if (self.m_objRFECommunicator.VerboseLevel > 5): 222 | sText = "New String: " 223 | nLength = len(strReceived) 224 | if (nLength > 10): 225 | nLength = 10 226 | if (nLength > 0): 227 | sText += strReceived[:nLength] 228 | print(sText.encode('utf-8')) 229 | elif (bLengthOK and bEEOT == False): 230 | #So we are here because the string doesn't end with the expected chars, but has the right length. 231 | #The most likely cause is a truncated string was received, and some chars are from next string, not 232 | #this one therefore we truncate the line to avoid being much larger, and start over again next time. 233 | nPosNextLine = strReceived.index("\r\n") 234 | if (nPosNextLine >= 0): 235 | strReceived = strReceived[nPosNextLine + 2:] 236 | elif (bEEOT == False): 237 | if (self.m_objRFECommunicator.VerboseLevel > 9): 238 | print("incomplete sweep") 239 | pass 240 | else: 241 | nEndPos = strReceived.find("\r\n") 242 | if (nEndPos >= 0): 243 | sNewLine = strReceived[:nEndPos] 244 | sLeftOver = strReceived[nEndPos + 2:] 245 | strReceived = sLeftOver 246 | self.m_hQueueLock.acquire() 247 | self.m_objQueue.put(sNewLine) 248 | self.m_hQueueLock.release() 249 | if (self.m_objRFECommunicator.VerboseLevel > 9): 250 | print("sNewLine: " + sNewLine) 251 | elif (self.m_objRFECommunicator.VerboseLevel > 5): 252 | print("DEBUG partial:" + strReceived) 253 | if(self.m_objRFECommunicator.Mode != RFE_Common.eMode.MODE_TRACKING): 254 | time.sleep(0.01) 255 | time.sleep(0.5) 256 | #print("ReceiveThreadfunc(): closing thread...") 257 | -------------------------------------------------------------------------------- /RFExplorer/__init__.py: -------------------------------------------------------------------------------- 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-21 RF Explorer Technologies SL, 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 | from RFExplorer.RFExplorer import RFECommunicator 26 | --------------------------------------------------------------------------------