mirror of https://github.com/f4exb/sdrangel.git
295 lines
14 KiB
Python
Executable File
295 lines
14 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import requests, json, traceback, sys
|
|
from optparse import OptionParser
|
|
|
|
base_url = "http://127.0.0.1:8091/sdrangel"
|
|
deviceset_url = ""
|
|
|
|
requests_methods = {
|
|
"GET": requests.get,
|
|
"PATCH": requests.patch,
|
|
"POST": requests.post,
|
|
"PUT": requests.put,
|
|
"DELETE": requests.delete
|
|
}
|
|
|
|
|
|
# ======================================================================
|
|
def getInputOptions():
|
|
|
|
parser = OptionParser(usage="usage: %%prog [-t]\n")
|
|
parser.add_option("-a", "--address", dest="address", help="address and port", metavar="ADDRESS", type="string")
|
|
parser.add_option("-d", "--device-index", dest="device_index", help="device set index", metavar="INDEX", type="int")
|
|
parser.add_option("-D", "--device-hwid", dest="device_hwid", help="device hardware id", metavar="HWID", type="string")
|
|
parser.add_option("-C", "--channel-id", dest="channel_id", help="channel id", metavar="ID", type="string", default="NFMDemod")
|
|
parser.add_option("-F", "--device-freq", dest="device_freq", help="device center frequency (kHz)", metavar="FREQ", type="int")
|
|
parser.add_option("-f", "--channel-freq", dest="channel_freq", help="channel center frequency (Hz)", metavar="FREQ", type="int")
|
|
parser.add_option("-s", "--sample-rate", dest="sample_rate", help="host to device sample rate (S/s)", metavar="RATE", type="int")
|
|
parser.add_option("-l", "--log2-interp", dest="log2_interp", help="log2 of interpolation factor", metavar="RATE", type="int")
|
|
parser.add_option("-L", "--log2-interp-hard", dest="log2_interp_hard", help="log2 of hardware interpolation factor", metavar="RATE", type="int")
|
|
parser.add_option("-A", "--antenna-path", dest="antenna_path", help="antenna path number", metavar="NUMBER", type="int")
|
|
parser.add_option("-c", "--create", dest="create", help="create a new device set", metavar="CREATE", action="store_true", default=False)
|
|
parser.add_option("--ppm", dest="lo_ppm", help="LO correction in ppm", metavar="FILENAME", type="float", default=0)
|
|
parser.add_option("--image", dest="image_file", help="image file for ATV modulator (sends image)", metavar="FILENAME", type="string")
|
|
parser.add_option("--video", dest="video_file", help="video file for ATV modulator (sends video)", metavar="FILENAME", type="string")
|
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
if options.address == None:
|
|
options.address = "127.0.0.1:8091"
|
|
|
|
if options.device_index == None:
|
|
options.device_index = 1
|
|
|
|
if options.device_hwid == None:
|
|
options.device_hwid = "FileInput"
|
|
|
|
if options.device_freq == None:
|
|
options.device_freq = 435000
|
|
|
|
if options.channel_freq == None:
|
|
options.channel_freq = 0
|
|
|
|
if options.sample_rate == None:
|
|
options.sample_rate = 2600000
|
|
|
|
if options.log2_interp == None:
|
|
options.log2_interp = 4
|
|
|
|
if options.log2_interp_hard == None:
|
|
options.log2_interp = 4
|
|
|
|
if options.antenna_path == None:
|
|
options.antenna_path = 1
|
|
|
|
return options
|
|
|
|
|
|
# ======================================================================
|
|
def printResponse(response):
|
|
content_type = response.headers.get("Content-Type", None)
|
|
if content_type is not None:
|
|
if "application/json" in content_type:
|
|
print(json.dumps(response.json(), indent=4, sort_keys=True))
|
|
elif "text/plain" in content_type:
|
|
print(response.text)
|
|
|
|
|
|
# ======================================================================
|
|
def callAPI(url, method, params, json, text):
|
|
request_method = requests_methods.get(method, None)
|
|
if request_method is not None:
|
|
r = request_method(url=base_url + url, params=params, json=json)
|
|
if r.status_code / 100 == 2:
|
|
print(text + " succeeded")
|
|
printResponse(r)
|
|
return r.json() # all 200 yield application/json response
|
|
else:
|
|
print(text + " failed")
|
|
printResponse(r)
|
|
return None
|
|
|
|
|
|
# ======================================================================
|
|
def setupBladeRFXB200(fc):
|
|
if fc < 50000:
|
|
return 5 # BLADERF_XB200_AUTO_3DB
|
|
elif fc < 54000:
|
|
return 0 # BLADERF_XB200_50M
|
|
elif fc < 144000:
|
|
return 5 # BLADERF_XB200_AUTO_3DB
|
|
elif fc < 148000:
|
|
return 1 # BLADERF_XB200_144M
|
|
elif fc < 222000:
|
|
return 5 # BLADERF_XB200_AUTO_3DB
|
|
elif fc < 225000:
|
|
return 2 # BLADERF_XB200_222M
|
|
else:
|
|
return 5 # BLADERF_XB200_AUTO_3DB
|
|
|
|
|
|
# ======================================================================
|
|
def setupDevice(options):
|
|
settings = callAPI(deviceset_url + "/device/settings", "GET", None, None, "Get device settings")
|
|
if settings is None:
|
|
exit(-1)
|
|
|
|
# calculate RF analog and FIR optimal bandpass filters bandwidths
|
|
lpFIRBW = options.sample_rate / (1 << options.log2_interp)
|
|
lpfBW = lpFIRBW * 1.2
|
|
|
|
if options.device_hwid == "BladeRF1":
|
|
settings['bladeRFOutputSettings']['centerFrequency'] = options.device_freq * 1000
|
|
settings['bladeRFOutputSettings']['devSampleRate'] = options.sample_rate
|
|
settings['bladeRFOutputSettings']['vga1'] = -20
|
|
settings['bladeRFOutputSettings']['vga2'] = 6
|
|
settings['bladeRFOutputSettings']['bandwidth'] = 1500 * 1000
|
|
settings['bladeRFOutputSettings']['log2Interp'] = options.log2_interp
|
|
settings['bladeRFOutputSettings']['xb200'] = 1 # assume XB200 is mounted
|
|
settings['bladeRFOutputSettings']['xb200Path'] = 1 if options.device_freq < 300000 else 0
|
|
settings['bladeRFOutputSettings']['xb200Filter'] = setupBladeRFXB200(options.device_freq)
|
|
elif options.device_hwid == "LimeSDR":
|
|
settings["limeSdrOutputSettings"]["antennaPath"] = options.antenna_path
|
|
settings["limeSdrOutputSettings"]["devSampleRate"] = options.sample_rate
|
|
settings["limeSdrOutputSettings"]["log2HardInterp"] = options.log2_interp_hard
|
|
settings["limeSdrOutputSettings"]["log2SoftInterp"] = options.log2_interp
|
|
settings["limeSdrOutputSettings"]["centerFrequency"] = options.device_freq * 1000 + 500000
|
|
settings["limeSdrOutputSettings"]["ncoEnable"] = 1
|
|
settings["limeSdrOutputSettings"]["ncoFrequency"] = -500000
|
|
settings["limeSdrOutputSettings"]["lpfBW"] = 4050000
|
|
settings["limeSdrOutputSettings"]["lpfFIRBW"] = 100000
|
|
settings["limeSdrOutputSettings"]["lpfFIREnable"] = 1
|
|
settings["limeSdrOutputSettings"]["gain"] = 30
|
|
elif options.device_hwid == "PlutoSDR":
|
|
settings["plutoSdrOutputSettings"]["antennaPath"] = options.antenna_path
|
|
settings["plutoSdrOutputSettings"]["devSampleRate"] = options.sample_rate
|
|
settings["plutoSdrOutputSettings"]["lpfFIRlog2Interp"] = options.log2_interp_hard
|
|
settings["plutoSdrOutputSettings"]["log2Interp"] = options.log2_interp
|
|
settings["plutoSdrOutputSettings"]["centerFrequency"] = options.device_freq * 1000
|
|
settings["plutoSdrOutputSettings"]["lpfBW"] = lpfBW
|
|
settings["plutoSdrOutputSettings"]["lpfFIRBW"] = lpFIRBW
|
|
settings["plutoSdrOutputSettings"]["lpfFIREnable"] = 1
|
|
settings["plutoSdrOutputSettings"]["att"] = -24 # -6 dB
|
|
elif options.device_hwid == "HackRF":
|
|
settings['hackRFOutputSettings']['LOppmTenths'] = round(options.lo_ppm * 10)
|
|
settings['hackRFOutputSettings']['centerFrequency'] = options.device_freq * 1000
|
|
settings['hackRFOutputSettings']['devSampleRate'] = options.sample_rate
|
|
settings['hackRFOutputSettings']['lnaExt'] = 0
|
|
settings['hackRFOutputSettings']['log2Interp'] = options.log2_interp
|
|
settings['hackRFOutputSettings']['vgaGain'] = 24
|
|
|
|
r = callAPI(deviceset_url + "/device/settings", "PATCH", None, settings, "Patch device settings")
|
|
if r is None:
|
|
exit(-1)
|
|
|
|
|
|
# ======================================================================
|
|
def setupChannel(options):
|
|
r = callAPI(deviceset_url + "/channel", "POST", None, {"channelType": options.channel_id, "direction": 1}, "Create modulator")
|
|
if r is None:
|
|
exit(-1)
|
|
|
|
settings = callAPI(deviceset_url + "/channel/0/settings", "GET", None, None, "Get modulator settings")
|
|
if settings is None:
|
|
exit(-1)
|
|
|
|
if options.channel_id == "NFMMod":
|
|
settings["NFMModSettings"]["title"] = "Test NFM"
|
|
settings["NFMModSettings"]["inputFrequencyOffset"] = options.channel_freq
|
|
settings["NFMModSettings"]["cwKeyer"]["text"] = "VVV DE F4EXB "
|
|
settings["NFMModSettings"]["cwKeyer"]["loop"] = 1
|
|
settings["NFMModSettings"]["cwKeyer"]["mode"] = 1 # text
|
|
settings["NFMModSettings"]["modAFInput"] = 4 # CW text
|
|
settings["NFMModSettings"]["toneFrequency"] = 600
|
|
elif options.channel_id == "AMMod":
|
|
settings["AMModSettings"]["title"] = "Test AM"
|
|
settings["AMModSettings"]["inputFrequencyOffset"] = options.channel_freq
|
|
settings["AMModSettings"]["cwKeyer"]["text"] = "VVV DE F4EXB "
|
|
settings["AMModSettings"]["cwKeyer"]["loop"] = 1
|
|
settings["AMModSettings"]["cwKeyer"]["mode"] = 1 # text
|
|
settings["AMModSettings"]["modAFInput"] = 4 # CW text
|
|
settings["AMModSettings"]["toneFrequency"] = 600
|
|
settings["AMModSettings"]["modFactor"] = 0.9
|
|
settings["AMModSettings"]["rfBandwidth"] = 7500
|
|
elif options.channel_id == "ATVMod":
|
|
settings["ATVModSettings"]["title"] = "Test ATV"
|
|
settings["ATVModSettings"]["inputFrequencyOffset"] = options.channel_freq
|
|
settings["ATVModSettings"]["rfBandwidth"] = 30000
|
|
settings["ATVModSettings"]["forceDecimator"] = 1 # This is to engage filter
|
|
|
|
if options.image_file is not None:
|
|
settings["ATVModSettings"]["imageFileName"] = options.image_file
|
|
settings["ATVModSettings"]["atvModInput"] = 6 # m_atvModulation
|
|
elif options.video_file is not None:
|
|
settings["ATVModSettings"]["videoFileName"] = options.video_file
|
|
settings["ATVModSettings"]["atvModInput"] = 7 # ATVModInputVideo
|
|
settings["ATVModSettings"]["videoPlayLoop"] = 1
|
|
settings["ATVModSettings"]["videoPlay"] = 1
|
|
else:
|
|
settings["ATVModSettings"]["atvModInput"] = 1 # ATVModInputHBars
|
|
|
|
settings["ATVModSettings"]["atvStd"] = 5 # ATVStdHSkip
|
|
settings["ATVModSettings"]["atvModulation"] = 1 # ATVModulationFM
|
|
settings["ATVModSettings"]["fps"] = 2
|
|
settings["ATVModSettings"]["nbLines"] = 90
|
|
settings["ATVModSettings"]["uniformLevel"] = 1.0 # 100% white
|
|
settings["ATVModSettings"]["fmExcursion"] = 0.2 # FM excursion is 20% of channel bandwidth
|
|
settings["ATVModSettings"]["overlayText"] = "F4EXB"
|
|
settings["ATVModSettings"]["showOverlayText"] = 1
|
|
elif options.channel_id == "SSBMod":
|
|
settings["SSBModSettings"]["title"] = "Test SSB"
|
|
settings["SSBModSettings"]["inputFrequencyOffset"] = options.channel_freq
|
|
settings["SSBModSettings"]["cwKeyer"]["text"] = "VVV DE F4EXB "
|
|
settings["SSBModSettings"]["cwKeyer"]["loop"] = 1
|
|
settings["SSBModSettings"]["cwKeyer"]["mode"] = 1 # text
|
|
settings["SSBModSettings"]["modAFInput"] = 4 # CW text
|
|
settings["SSBModSettings"]["toneFrequency"] = 600
|
|
settings["SSBModSettings"]["bandwidth"] = 1000
|
|
settings["SSBModSettings"]["lowCut"] = 300
|
|
elif options.channel_id == "UDPSource":
|
|
settings["UDPSourceSettings"]["title"] = "Test UDP Source"
|
|
settings["UDPSourceSettings"]["inputFrequencyOffset"] = options.channel_freq
|
|
settings["UDPSourceSettings"]["rfBandwidth"] = 12500
|
|
settings["UDPSourceSettings"]["fmDeviation"] = 6000
|
|
settings["UDPSourceSettings"]["autoRWBalance"] = 1
|
|
settings["UDPSourceSettings"]["stereoInput"] = 0
|
|
settings["UDPSourceSettings"]["udpAddress"] = "127.0.0.1"
|
|
settings["UDPSourceSettings"]["udpPort"] = 9998
|
|
settings["UDPSourceSettings"]["inputSampleRate"] = 48000
|
|
settings["UDPSourceSettings"]["sampleFormat"] = 1 # FormatNFM
|
|
settings["UDPSourceSettings"]["gainIn"] = 2.5
|
|
settings["UDPSourceSettings"]["gainOut"] = 2.8
|
|
elif options.channel_id == "WFMMod":
|
|
settings["WFMModSettings"]["title"] = "Test WFM"
|
|
settings["WFMModSettings"]["inputFrequencyOffset"] = options.channel_freq
|
|
settings["WFMModSettings"]["cwKeyer"]["text"] = "VVV DE F4EXB "
|
|
settings["WFMModSettings"]["cwKeyer"]["loop"] = 1
|
|
settings["WFMModSettings"]["cwKeyer"]["mode"] = 1 # text
|
|
settings["WFMModSettings"]["modAFInput"] = 4 # CW text
|
|
settings["WFMModSettings"]["toneFrequency"] = 600
|
|
settings["WFMModSettings"]["fmDeviation"] = 25000
|
|
settings["WFMModSettings"]["rfBandwidth"] = 75000
|
|
settings["WFMModSettings"]["afBandwidth"] = 3000
|
|
|
|
r = callAPI(deviceset_url + "/channel/0/settings", "PATCH", None, settings, "Change modulator")
|
|
if r is None:
|
|
exit(-1)
|
|
|
|
|
|
# ======================================================================
|
|
def main():
|
|
try:
|
|
options = getInputOptions()
|
|
|
|
global base_url
|
|
base_url = "http://%s/sdrangel" % options.address
|
|
|
|
if options.create:
|
|
r = callAPI("/deviceset", "POST", {"direction": 1}, None, "Add Tx device set")
|
|
if r is None:
|
|
exit(-1)
|
|
|
|
global deviceset_url
|
|
deviceset_url = "/deviceset/%d" % options.device_index
|
|
|
|
r = callAPI(deviceset_url + "/device", "PUT", None, {"hwType": "%s" % options.device_hwid, "direction": 1}, "setup device on Tx device set")
|
|
if r is None:
|
|
exit(-1)
|
|
|
|
setupDevice(options)
|
|
setupChannel(options)
|
|
|
|
r = callAPI(deviceset_url + "/device/run", "POST", None, None, "Start running device")
|
|
if r is None:
|
|
exit(-1)
|
|
|
|
except Exception as ex:
|
|
tb = traceback.format_exc()
|
|
print >> sys.stderr, tb
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|