# -*- coding: utf-8 -*- # # DPD Calculation Engine: updates ODR-DabMod's predistortion block. # # http://www.opendigitalradio.org # Licence: The MIT License, see notice at the end of this file """ This module is used to change settings of ODR-DabMod using the ZMQ remote control socket. """ import zmq import logging import numpy as np class Adapt: """Uses the ZMQ remote control to change parameters of the DabMod Parameters ---------- port : int Port at which the ODR-DabMod is listening to connect the ZMQ remote control. """ def __init__(self, port, coef_path): logging.debug("Instantiate Adapt object") self.port = port self.coef_path = coef_path self.host = "localhost" self._context = zmq.Context() def _connect(self): """Establish the connection to ODR-DabMod using a ZMQ socket that is in request mode (Client). Returns a socket""" sock = self._context.socket(zmq.REQ) sock.connect("tcp://%s:%d" % (self.host, self.port)) sock.send(b"ping") data = [el.decode() for el in sock.recv_multipart()] if data != ['ok']: raise RuntimeError( "Could not ping server at %s %d: %s" % (self.host, self.port, data)) return sock def send_receive(self, message): """Send a message to ODR-DabMod. It always returns the answer ODR-DabMod sends back. An example message could be "get uhd txgain" or "set uhd txgain 50" Parameter --------- message : str The message string that will be sent to the receiver. """ sock = self._connect() logging.debug("Send message: %s" % message) msg_parts = message.split(" ") for i, part in enumerate(msg_parts): if i == len(msg_parts) - 1: f = 0 else: f = zmq.SNDMORE sock.send(part.encode(), flags=f) data = [el.decode() for el in sock.recv_multipart()] logging.debug("Received message: %s" % message) return data def set_txgain(self, gain): """Set a new txgain for the ODR-DabMod. Parameters ---------- gain : int new TX gain, in the same format as ODR-DabMod's config file """ # TODO this is specific to the B200 if gain < 0 or gain > 89: raise ValueError("Gain has to be in [0,89]") return self.send_receive("set uhd txgain %.4f" % float(gain)) def get_txgain(self): """Get the txgain value in dB for the ODR-DabMod.""" # TODO handle failure return float(self.send_receive("get uhd txgain")[0]) def set_rxgain(self, gain): """Set a new rxgain for the ODR-DabMod. Parameters ---------- gain : int new RX gain, in the same format as ODR-DabMod's config file """ # TODO this is specific to the B200 if gain < 0 or gain > 89: raise ValueError("Gain has to be in [0,89]") return self.send_receive("set uhd rxgain %.4f" % float(gain)) def get_rxgain(self): """Get the rxgain value in dB for the ODR-DabMod.""" # TODO handle failure return float(self.send_receive("get uhd rxgain")[0]) def set_digital_gain(self, gain): """Set a new rxgain for the ODR-DabMod. Parameters ---------- gain : int new RX gain, in the same format as ODR-DabMod's config file """ msg = "set gain digital %.5f" % gain return self.send_receive(msg) def get_digital_gain(self): """Get the rxgain value in dB for the ODR-DabMod.""" # TODO handle failure return float(self.send_receive("get gain digital")[0]) def _read_coef_file(self, path): """Load the coefficients from the file in the format given in the README, return ([AM coef], [PM coef])""" coefs_am_out = [] coefs_pm_out = [] f = open(path, 'r') lines = f.readlines() n_coefs = int(lines[0]) coefs = [float(l) for l in lines[1:]] i = 0 for c in coefs: if i < n_coefs: coefs_am_out.append(c) elif i < 2*n_coefs: coefs_pm_out.append(c) else: raise ValueError( "Incorrect coef file format: too many coefficients in {}, should be {}, coefs are {}" .format(path, n_coefs, coefs)) i += 1 f.close() return (np.array(coefs_am_out, dtype=np.float32), np.array(coefs_pm_out, dtype=np.float32)) def get_coefs(self): return self._read_coef_file(self.coef_path) def _write_coef_file(self, coefs_am, coefs_pm, path): assert(len(coefs_am) == len(coefs_pm)) f = open(path, 'w') f.write("{}\n".format(len(coefs_am))) for coef in coefs_am: f.write("{}\n".format(coef)) for coef in coefs_pm: f.write("{}\n".format(coef)) f.close() def set_coefs(self, coefs_am, coefs_pm): self._write_coef_file(coefs_am, coefs_pm, self.coef_path) self.send_receive("set memlesspoly coeffile {}".format(self.coef_path)) # The MIT License (MIT) # # Copyright (c) 2017 Andreas Steger, Matthias P. Braendli # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE.