| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | #!/usr/bin/env python3 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  | # pylint: disable=line-too-long | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | Quick control of configuration for receiving QO-100 DATV | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  | import traceback | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | from optparse import OptionParser # pylint: disable=deprecated-module | 
					
						
							|  |  |  | import requests | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | import sdrangel | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  | BASE_URL_PATTERN = "http://{0}/sdrangel" | 
					
						
							|  |  |  | BASE_URL = None | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  | def get_input_options(): | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |     """ Parse options """ | 
					
						
							|  |  |  | # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |     parser = OptionParser(usage="usage: %%prog [-t]\n") | 
					
						
							|  |  |  |     parser.add_option("-a", "--address", dest="address", help="Address and port. Default: 127.0.0.1:8091", metavar="ADDRESS", type="string") | 
					
						
							|  |  |  |     parser.add_option("-d", "--device", dest="device_index", help="Index of device set. Default 0", metavar="INT", type="int") | 
					
						
							|  |  |  |     parser.add_option("-c", "--channel", dest="channel_index", help="Index of DATV demod channel. Default 0", metavar="INT", type="int") | 
					
						
							|  |  |  |     parser.add_option("-f", "--frequency", dest="frequency", help="Device center frequency (kHz). Mandatory", metavar="INT", type="int") | 
					
						
							|  |  |  |     parser.add_option("-s", "--symbol-rate", dest="symbol_rate", help="Symbol rate (kS/s). Mandatory", metavar="INT", type="int") | 
					
						
							| 
									
										
										
										
											2022-07-24 19:31:21 +02:00
										 |  |  |     parser.add_option("-n", "--no-decim", dest="no_decim", help="Do not change decimation", action="store_true") | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     (options, args) = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if options.address is None: | 
					
						
							|  |  |  |         options.address = "127.0.0.1:8091" | 
					
						
							|  |  |  |     if options.device_index is None: | 
					
						
							|  |  |  |         options.device_index = 0 | 
					
						
							|  |  |  |     if options.channel_index is None: | 
					
						
							|  |  |  |         options.channel_index = 0 | 
					
						
							|  |  |  |     if options.frequency is None: | 
					
						
							|  |  |  |         raise RuntimeError("Frequency (-f or --frequency) is mandatory") | 
					
						
							|  |  |  |     if options.symbol_rate is None: | 
					
						
							|  |  |  |         raise RuntimeError("Symbol rate (-s or --symbol-rate) is mandatory") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return options | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  | class Controller: | 
					
						
							|  |  |  |     """ Controller main class """ | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |     def __init__(self, options): | 
					
						
							|  |  |  |         self.options = options | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |         self.device_hwtype = None | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |     def change_device_center_frequency(self, content, new_frequency): | 
					
						
							|  |  |  |         """ Change device center frequency searching recursively """ | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |     # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |         for k in content: | 
					
						
							|  |  |  |             if isinstance(content[k], dict): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |                 self.change_device_center_frequency(content[k], new_frequency) | 
					
						
							|  |  |  |             elif k == sdrangel.DEVICE_TYPES[self.device_hwtype]["cf_key"]: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |                 content[k] = new_frequency | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							|  |  |  |     def change_decim(self, content, new_decim): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |         """ Change device log2 decimation searching recursively """ | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |     # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |         for k in content: | 
					
						
							|  |  |  |             if isinstance(content[k], dict): | 
					
						
							|  |  |  |                 self.change_decim(content[k], new_decim) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |             elif k == sdrangel.DEVICE_TYPES[self.device_hwtype]["decim_key"]: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |                 content[k] = new_decim | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |     # ====================================================================== | 
					
						
							|  |  |  |     def change_channel_center_frequency(self, content, new_frequency): | 
					
						
							|  |  |  |         """ Change DATV channel center frequency searching recursively """ | 
					
						
							|  |  |  |     # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |         for k in content: | 
					
						
							|  |  |  |             if isinstance(content[k], dict): | 
					
						
							|  |  |  |                 self.change_channel_center_frequency(content[k], new_frequency) | 
					
						
							|  |  |  |             elif k == "centerFrequency": | 
					
						
							|  |  |  |                 content[k] = new_frequency | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |     # ====================================================================== | 
					
						
							|  |  |  |     def change_rfbw(self, content, new_rfbw): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |         """ Change DATV channel RF bandwidth searching recursively """ | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |     # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |         for k in content: | 
					
						
							|  |  |  |             if isinstance(content[k], dict): | 
					
						
							|  |  |  |                 self.change_rfbw(content[k], new_rfbw) | 
					
						
							|  |  |  |             elif k == "rfBandwidth": | 
					
						
							|  |  |  |                 content[k] = new_rfbw | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							|  |  |  |     def change_symbol_rate(self, content, new_symbol_rate): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |         """ Change DATV channel symbol rate searching recursively """ | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |     # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |         for k in content: | 
					
						
							|  |  |  |             if isinstance(content[k], dict): | 
					
						
							|  |  |  |                 self.change_symbol_rate(content[k], new_symbol_rate) | 
					
						
							|  |  |  |             elif k == "symbolRate": | 
					
						
							|  |  |  |                 content[k] = new_symbol_rate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							|  |  |  |     def get_device_sr_and_decim(self, hwtype, settings): | 
					
						
							|  |  |  |         """ Return device sample rate and decimation """ | 
					
						
							|  |  |  |     # ---------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |         decim_key = sdrangel.DEVICE_TYPES[self.device_hwtype]["decim_key"] | 
					
						
							|  |  |  |         if hwtype in ("Airspy", "AirspyHF"): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             sr_index = settings.get("devSampleRateIndex", 0) | 
					
						
							|  |  |  |             if sr_index == 1: | 
					
						
							|  |  |  |                 sr = 3000000 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 sr = 6000000 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |             log2_decim = settings.get(decim_key, 0) | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             sr = settings.get("devSampleRate", 0) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |             log2_decim = settings.get(decim_key, 0) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         return sr, 1<<log2_decim | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def calc_decim(device_sr, symbol_rate): | 
					
						
							|  |  |  |         """ Calculate ideal decimation and return as log2 """ | 
					
						
							|  |  |  |     # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |         quot = device_sr // (2*symbol_rate*1000) | 
					
						
							|  |  |  |         for i in range(6): | 
					
						
							|  |  |  |             if quot < 1<<i: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         return 0 if i < 1 else i - 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |     def set_device(self): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         """ Set the device """ | 
					
						
							|  |  |  |     # ---------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |         device_settings_url = f"{BASE_URL}/deviceset/{self.options.device_index}/device/settings" | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         r = requests.get(url=device_settings_url, timeout=10) | 
					
						
							|  |  |  |         if r.status_code // 100 == 2: # OK | 
					
						
							|  |  |  |             rj = r.json() | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |             self.device_hwtype = rj.get("deviceHwType", None) | 
					
						
							|  |  |  |             device_settings = rj.get(sdrangel.DEVICE_TYPES[self.device_hwtype]["settings"], None) | 
					
						
							|  |  |  |             device_sr, device_decim = self.get_device_sr_and_decim(self.device_hwtype, device_settings) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             new_decim = self.calc_decim(device_sr, self.options.symbol_rate) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             print(f"sr: {device_sr} S/s decim: {device_decim} new decim: {1<<new_decim}") | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             if not self.options.no_decim: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |                 self.change_decim(rj, new_decim) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             self.change_device_center_frequency(rj, self.options.frequency*1000) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             r = requests.patch(url=device_settings_url, json=rj, timeout=10) | 
					
						
							|  |  |  |             if r.status_code / 100 == 2: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |                 print(f'set_device: changed #{self.options.device_index}: frequency: {self.options.frequency} kHz log2Decim: {new_decim} No decim: {self.options.no_decim}') | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |                 raise RuntimeError(f'set_device: failed to change device {self.options.device_index} with error {r.status_code}') | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             raise RuntimeError(f"Cannot get device info for device set index {self.options.device_index} HTTP return code {r.status_code}") | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # ====================================================================== | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |     def set_channel(self): | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         """ Set the channel - assume DATV demod """ | 
					
						
							|  |  |  |     # ---------------------------------------------------------------------- | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |         channel_settings_url = f"{BASE_URL}/deviceset/{self.options.device_index}/channel/{self.options.channel_index}/settings" | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         r = requests.get(url=channel_settings_url, timeout=10) | 
					
						
							|  |  |  |         if r.status_code // 100 == 2: # OK | 
					
						
							|  |  |  |             rj = r.json() | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             rfbw = round(1.5*self.options.symbol_rate)*1000 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:38:56 +01:00
										 |  |  |             self.change_channel_center_frequency(rj, 0) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             self.change_rfbw(rj, rfbw) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             self.change_symbol_rate(rj, self.options.symbol_rate*1000) | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             r = requests.patch(url=channel_settings_url, json=rj, timeout=10) | 
					
						
							|  |  |  |             if r.status_code / 100 == 2: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |                 print(f'set_channel: changed {self.options.device_index}:{self.options.channel_index}: rfbw: {rfbw} Hz symbol rate: {self.options.symbol_rate} kS/s') | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |                 raise RuntimeError(f'set_device: failed to change channel {self.options.device_index}:{self.options.channel_index} with error {r.status_code}') | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |             raise RuntimeError(f"Cannot get channel info for channel {self.options.device_index}:{self.options.channel_index} HTTP return code {r.status_code}") | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     """ Main program """ | 
					
						
							|  |  |  | # ---------------------------------------------------------------------- | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         options = get_input_options() | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         global BASE_URL # pylint: disable=global-statement | 
					
						
							|  |  |  |         BASE_URL =  BASE_URL_PATTERN.format(options.address) | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |         print(f"Frequency: 10{options.frequency/1000} MHz Symbol rate: {options.symbol_rate} kS/s") | 
					
						
							| 
									
										
										
										
											2022-11-19 12:46:27 +01:00
										 |  |  |         controller = Controller(options) | 
					
						
							|  |  |  |         controller.set_device() | 
					
						
							|  |  |  |         controller.set_channel() | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |     except Exception as ex: # pylint: disable=broad-except | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |         tb = traceback.format_exc() | 
					
						
							| 
									
										
										
										
											2022-11-19 12:14:51 +01:00
										 |  |  |         print(f"Exception caught {ex}") | 
					
						
							| 
									
										
										
										
											2021-04-03 06:58:03 +02:00
										 |  |  |         print(tb, file=sys.stderr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     main() |