class DecoderDialog(QDialog): def __init__(self, decodings, signals, project_manager: ProjectManager, parent=None): """ :type decodings: list of Encoding :type signals: list of Signal """ # Init super().__init__(parent) self.ui = Ui_Decoder() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) # Variables self.old_inpt_txt = "" self.old_carrier_txt = "" self.old_decoderchain = [] self.active_message = "" self.old_cutmark = "" self.old_morse = (1, 3) self.project_manager = project_manager # Initialize encoder self.decodings = decodings self.ui.combobox_decodings.clear() for decoding in self.decodings: self.ui.combobox_decodings.addItem(decoding.name) self.chainstr = [] self.chainoptions = {} self.set_e() # Signals self.signals = signals if signals is not None else [] for signal in signals: if signal: self.ui.combobox_signals.addItem(signal.name) # Function lists self.ui.basefunctions.addItem(constants.DECODING_EDGE) self.ui.basefunctions.addItem(constants.DECODING_MORSE) self.ui.basefunctions.addItem(constants.DECODING_SUBSTITUTION) self.ui.basefunctions.addItem(constants.DECODING_EXTERNAL) self.ui.additionalfunctions.addItem(constants.DECODING_INVERT) self.ui.additionalfunctions.addItem(constants.DECODING_DIFFERENTIAL) self.ui.additionalfunctions.addItem(constants.DECODING_BITORDER) self.ui.additionalfunctions.addItem(constants.DECODING_REDUNDANCY) self.ui.additionalfunctions.addItem(constants.DECODING_CARRIER) self.ui.additionalfunctions.addItem(constants.DECODING_DATAWHITENING) self.ui.additionalfunctions.addItem(constants.DECODING_ENOCEAN) self.ui.additionalfunctions.addItem(constants.DECODING_CUT) # Presets self.setWindowTitle("Decoding") self.setWindowIcon(QIcon(":/icons/icons/decoding.svg")) self.setAcceptDrops(True) self.inpt_text = "10010110" self.ui.inpt.setText(self.inpt_text) self.ui.optionWidget.setCurrentIndex(0) self.decoder_update() self.ui.substitution.setColumnCount(2) self.ui.substitution.setRowCount(self.ui.substitution_rows.value()) self.ui.substitution.setHorizontalHeaderLabels(['From', 'To']) self.ui.substitution.setColumnWidth(0, 190) self.ui.substitution.setColumnWidth(1, 190) self.ui.saveas.setVisible(False) # Connects self.create_connects() try: self.restoreGeometry(constants.SETTINGS.value("{}/geometry".format(self.__class__.__name__))) except TypeError: pass def create_connects(self): self.ui.inpt.textChanged.connect(self.decoder_update) self.ui.multiple.valueChanged.connect(self.handle_multiple_changed) self.ui.carrier.textChanged.connect(self.handle_carrier_changed) self.ui.substitution_rows.valueChanged.connect(self.handle_substitution_rows_changed) self.ui.substitution.itemChanged.connect(self.handle_substitution_changed) self.ui.btnChooseDecoder.clicked.connect(self.choose_decoder) self.ui.btnChooseEncoder.clicked.connect(self.choose_encoder) self.ui.external_decoder.textEdited.connect(self.handle_external) self.ui.external_encoder.textEdited.connect(self.handle_external) self.ui.datawhitening_sync.textEdited.connect(self.handle_datawhitening) self.ui.datawhitening_polynomial.textEdited.connect(self.handle_datawhitening) self.ui.datawhitening_overwrite_crc.clicked.connect(self.handle_datawhitening) self.ui.decoderchain.itemChanged.connect(self.decoderchainUpdate) self.ui.decoderchain.internalMove.connect(self.decoderchainUpdate) self.ui.decoderchain.deleteElement.connect(self.deleteElement) self.ui.decoderchain.currentRowChanged.connect(self.on_decoder_chain_current_row_changed) self.ui.basefunctions.currentRowChanged.connect(self.on_base_functions_current_row_changed) self.ui.additionalfunctions.currentRowChanged.connect(self.on_additional_functions_current_row_changed) self.ui.combobox_decodings.currentIndexChanged.connect(self.set_e) self.ui.combobox_signals.currentIndexChanged.connect(self.set_signal) self.ui.saveas.clicked.connect(self.saveas) self.ui.delete_decoding.clicked.connect(self.delete_decoding) self.ui.rB_delbefore.clicked.connect(self.handle_cut) self.ui.rB_delafter.clicked.connect(self.handle_cut) self.ui.rB_delbeforepos.clicked.connect(self.handle_cut) self.ui.rB_delafterpos.clicked.connect(self.handle_cut) self.ui.cutmark.textEdited.connect(self.handle_cut) self.ui.cutmark2.valueChanged.connect(self.handle_cut) self.ui.morse_low.valueChanged.connect(self.handle_morse_changed) self.ui.morse_high.valueChanged.connect(self.handle_morse_changed) self.ui.morse_wait.valueChanged.connect(self.handle_morse_changed) def closeEvent(self, event: QCloseEvent): constants.SETTINGS.setValue("{}/geometry".format(self.__class__.__name__), self.saveGeometry()) super().closeEvent(event) def choose_decoder(self): f, ok = QFileDialog.getOpenFileName(self, self.tr("Choose decoder program"), QDir.homePath()) if f and ok: self.ui.external_decoder.setText(f) self.handle_external() def choose_encoder(self): f, ok = QFileDialog.getOpenFileName(self, self.tr("Choose encoder program"), QDir.homePath()) if f and ok: self.ui.external_encoder.setText(f) self.handle_external() def save_to_file(self): if self.project_manager.project_file: prefix = os.path.realpath(os.path.dirname( self.project_manager.project_file)) else: prefix = os.path.realpath(os.path.join(constants.SETTINGS.fileName(), "..")) f = open(os.path.join(prefix, constants.DECODINGS_FILE), "w") if not f: return for i in range(0, self.ui.combobox_decodings.count()): str = "" for j in self.decodings[i].get_chain(): str += repr(j) + ", " str += "\n" f.write(str) f.close() def saveas(self): # Ask for a name name, ok = QInputDialog.getText(self, self.tr("Save decoding"), self.tr("Please enter a name:"), QLineEdit.Normal, self.e.chain[0]) if ok and name != "": self.e.chain[0] = name self.decoderchainUpdate() # If name is already there, overwrite existing for i in range(0, len(self.decodings)): if name == self.decodings[i].name: self.ui.combobox_decodings.setCurrentIndex(i) self.decodings[i] = Encoding(self.chainstr) self.set_e() self.ui.saveas.setVisible(False) self.save_to_file() return self.decodings.append(Encoding(self.chainstr)) self.ui.combobox_decodings.addItem(self.chainstr[0]) self.ui.combobox_decodings.setCurrentIndex(self.ui.combobox_decodings.count() - 1) self.set_e() self.save_to_file() def delete_decoding(self): num = self.ui.combobox_decodings.currentIndex() if num >= 0: reply = QMessageBox.question(self, self.tr("Delete Decoding?"), self.tr("Do you really want to delete " + "'{}'?".format( self.decodings[num].name)), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.decodings.pop(num) self.ui.combobox_decodings.removeItem(num) self.save_to_file() def set_e(self): if self.ui.combobox_decodings.count() < 1: # Empty list return self.e = copy.deepcopy(self.decodings[self.ui.combobox_decodings.currentIndex()]) """:type: encoding """ chain = self.e.get_chain() self.ui.decoderchain.clear() self.chainoptions.clear() last_i = "" for i in chain: if i in [constants.DECODING_INVERT, constants.DECODING_ENOCEAN, constants.DECODING_DIFFERENTIAL, constants.DECODING_REDUNDANCY, constants.DECODING_CARRIER, constants.DECODING_BITORDER, constants.DECODING_EDGE, constants.DECODING_DATAWHITENING, constants.DECODING_SUBSTITUTION, constants.DECODING_EXTERNAL, constants.DECODING_CUT, constants.DECODING_MORSE, constants.DECODING_DISABLED_PREFIX]: self.ui.decoderchain.addItem(i) self.decoderchainUpdate() last_i = self.ui.decoderchain.item(self.ui.decoderchain.count() - 1).text() else: if any(x in last_i for x in [constants.DECODING_REDUNDANCY, constants.DECODING_CARRIER, constants.DECODING_SUBSTITUTION, constants.DECODING_EXTERNAL, constants.DECODING_DATAWHITENING, constants.DECODING_CUT, constants.DECODING_MORSE]): self.chainoptions[last_i] = i self.decoderchainUpdate() self.decoder_update() self.ui.saveas.setVisible(False) def decoderchainUpdate(self): # for i in range (0, self.ui.decoderchain.count()): # print(i, "->", self.ui.decoderchain.item(i).text()) # print() self.ui.saveas.setVisible(True) self.eliminateDuplicates() self.chainstr = [self.e.name] for i in range(0, self.ui.decoderchain.count()): op = self.ui.decoderchain.item(i).text() # Is this function disabled? if constants.DECODING_DISABLED_PREFIX in op: continue self.chainstr.append(op) # Add parameters to chainstr if constants.DECODING_REDUNDANCY in op: # Read Multiple Value if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = 2 self.chainstr.append(2) # Default elif constants.DECODING_CARRIER in op: # Read Carrier Field and add string to chainstr if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_SUBSTITUTION in op: # Add substitution string to chainstr: Format = src0:dst0;src1:dst1;... if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_EXTERNAL in op: # Add program path's string to chainstr: Format = decoder;encoder if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_DATAWHITENING in op: # Add Data Whitening Parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("0xe9cae9ca;0x21;0") # Default elif constants.DECODING_CUT in op: # Add cut parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("0;1010") # Default elif constants.DECODING_MORSE in op: # Add morse parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("1;3;1") # Default self.e.set_chain(self.chainstr) self.decoder_update() def deleteElement(self): if self.ui.decoderchain.count() == 0: # Clear all self.chainoptions.clear() else: self.chainoptions.pop(self.ui.decoderchain.active_element_text, None) self.decoderchainUpdate() def eliminateDuplicates(self): decoderchain_count = self.ui.decoderchain.count() olddecoderchain_count = len(self.old_decoderchain) # Special case for 1 element (add " ") if decoderchain_count == 1: tmp = self.ui.decoderchain.item(0).text() if tmp[-1] != " " and not tmp[-1].isnumeric(): self.ui.decoderchain.takeItem(0) self.ui.decoderchain.insertItem(0, tmp + " ") # Ignore internal move (same count()) and removed elements and lists < 2 // len(self.old_decoderchain)+1 == self.ui.decoderchain.count() if decoderchain_count > 1 and decoderchain_count > olddecoderchain_count: elem = 0 while elem < olddecoderchain_count: if self.ui.decoderchain.item(elem).text() == self.old_decoderchain[elem]: elem += 1 else: break # Count number of current elements and append string "#<num>" to current text, if num > 1 txt = self.ui.decoderchain.item(elem).text() num = 0 for i in range(0, decoderchain_count): if txt in self.ui.decoderchain.item(i).text(): num += 1 if num > 1: tmp_txt = txt + " #" + str(num) else: tmp_txt = txt + " " # Check duplicate names dup = False for i in range(0, decoderchain_count): if self.ui.decoderchain.item(i).text() == tmp_txt: dup = True break if dup: for i in range(1, num): if i > 1: tmp_txt = txt + " #" + str(i) else: tmp_txt = txt + " " dup = False for j in range(0, decoderchain_count): if self.ui.decoderchain.item(j).text() == tmp_txt: dup = True break if not dup: break # Replace current element with new "text #<num>" if not dup: self.ui.decoderchain.takeItem(elem) self.ui.decoderchain.insertItem(elem, tmp_txt) # Save current decoderchain to old_decoderchain self.old_decoderchain = [] for i in range(0, decoderchain_count): self.old_decoderchain.append(self.ui.decoderchain.item(i).text()) def decoder_update(self): # Only allow {0, 1} signaltype = self.ui.combobox_signals.currentIndex() inpt_txt = self.ui.inpt.text() if signaltype == 0: if inpt_txt.count("0") + inpt_txt.count("1") < len(inpt_txt): self.ui.inpt.setText(self.old_inpt_txt) else: self.old_inpt_txt = inpt_txt # Write decoded bits bit = self.e.str2bit(self.ui.inpt.text()) decoded = self.e.bit2str(self.e.decode(bit)) errors = "[Decoding Errors = " + str(self.e.analyze(bit)[0]) + "]" self.ui.decoding_errors_label.setText(errors) self.ui.output.setText(decoded) self.ui.output.setCursorPosition(0) if len(decoded) > 0: if signaltype == 0: temp_signal = SignalSceneManager.create_rectangle(inpt_txt)[0] self.ui.graphicsView_signal.setScene(temp_signal) self.ui.graphicsView_signal.update() temp_decoded = SignalSceneManager.create_rectangle(decoded)[0] self.ui.graphicsView_decoded.setScene(temp_decoded) self.ui.graphicsView_decoded.update() @pyqtSlot(int) def on_base_functions_current_row_changed(self, index: int): if self.ui.basefunctions.currentItem().text() is not None: self.ui.decoderchain.setCurrentRow(-1) self.set_information(0) else: self.ui.optionWidget.setCurrentIndex(0) self.ui.info.clear() @pyqtSlot(int) def on_additional_functions_current_row_changed(self, index: int): if self.ui.additionalfunctions.currentItem() is not None: self.ui.decoderchain.setCurrentRow(-1) self.set_information(1) else: self.ui.optionWidget.setCurrentIndex(0) self.ui.info.clear() @pyqtSlot(int) def on_decoder_chain_current_row_changed(self, index: int): if self.ui.decoderchain.currentItem() is not None: self.set_information(2) else: self.ui.optionWidget.setCurrentIndex(0) self.ui.info.clear() def set_information(self, mode: int): # Presets decoderEdit = False self.ui.optionWidget.setCurrentIndex(0) txt = "" # Determine selected element if mode == 0: element = self.ui.basefunctions.currentItem().text() txt += element + ":\n" elif mode == 1: element = self.ui.additionalfunctions.currentItem().text() txt += element + ":\n" elif mode == 2: decoderEdit = True txt = "## DECODING PROCESS ##\n\n" element = self.ui.decoderchain.currentItem().text() if element[-1] == " ": elementname = element[0:-1] else: elementname = element txt += elementname + ":\n" self.active_message = element # Remove "[Disabled] " for further tasks if constants.DECODING_DISABLED_PREFIX in element: element = element[len(constants.DECODING_DISABLED_PREFIX):] # Write info text and show options if constants.DECODING_EDGE in element: txt += "Trigger on signal edge, i.e. the transition between low and high.\n" \ "- Low to High (01) is 1\n" \ "- High to Low (10) is 0" elif constants.DECODING_SUBSTITUTION in element: txt += "A set of manual defined signal sequences FROM (e.g. 110, 100) is replaced by another set of " \ "sequences TO (e.g. 01, 10). Note that all FROM entries must have the same length, otherwise " \ "the result is unpredictable! (For TX: all TO entries must have the same length)" self.ui.optionWidget.setCurrentIndex(3) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) else: if element in self.chainoptions: values = self.chainoptions[element] if values == "": self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) else: arrs = self.e.get_subst_array(values) if len(arrs[0]) == len(arrs[1]): self.ui.substitution_rows.setValue(len(arrs[0])) self.ui.substitution.setRowCount(len(arrs[0])) for i in range(0, len(arrs[0])): self.ui.substitution.setItem(i, 0, QTableWidgetItem(self.e.bit2str(arrs[0][i]))) self.ui.substitution.setItem(i, 1, QTableWidgetItem(self.e.bit2str(arrs[1][i]))) else: self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) self.ui.substitution.setEnabled(decoderEdit) self.ui.substitution_rows.setEnabled(decoderEdit) elif constants.DECODING_EXTERNAL in element: txt += "The decoding (and encoding) process is delegated to external programs or scripts via parameter.\n" \ "Example: Given the signal 10010110, your program is called as './decoder 10010110'. Your program " \ "computes and prints a corresponding set of 0s and 1s which is fed back into the decoding process. " self.ui.optionWidget.setCurrentIndex(4) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") else: decstr, encstr = value.split(";") self.ui.external_decoder.setText(decstr) self.ui.external_encoder.setText(encstr) else: self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") self.ui.external_decoder.setEnabled(decoderEdit) self.ui.external_encoder.setEnabled(decoderEdit) self.ui.btnChooseDecoder.setEnabled(decoderEdit) self.ui.btnChooseEncoder.setEnabled(decoderEdit) elif constants.DECODING_INVERT in element: txt += "All bits are inverted, i.e. 0->1 and 1->0." elif constants.DECODING_ENOCEAN in element: txt += "Remove Wireless Short-Packet (WSP) encoding that is used by EnOcean standard." elif constants.DECODING_DIFFERENTIAL in element: txt += "Every transition between low and high (0->1 or 1->0) becomes 1, no transition (0->0 or 1->1) remains 0.\n" \ "The first signal bit is regarded as start value and directly copied.\n" \ "Example: 0011 becomes 0010 [0|(0->0)|(0->1)|(1->1)]." elif constants.DECODING_BITORDER in element: txt += "Every byte (8 bit) is reversed, i.e. the order of the bits 01234567 (e.g. least significant bit first) " \ "is changed to 76543210 (e.g. most significant bit first)." elif constants.DECODING_REDUNDANCY in element: txt += "If the source signal always has multiple redundant bits for one bit (e.g. 1111=1, 0000=0), the " \ "redundancy is removed here. You have to define the number of redundant bits." self.ui.optionWidget.setCurrentIndex(1) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.multiple.setValue(2) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.multiple.setValue(2) else: self.ui.multiple.setValue(int(value)) else: self.ui.multiple.setValue(2) self.ui.multiple.setEnabled(decoderEdit) elif constants.DECODING_MORSE in element: txt += "If the signal is a morse code, e.g. 00111001001110011100, where information are " \ "transported with long and short sequences of 1 (0 just for padding), then this " \ "decoding evaluates those sequences (Example output: 1011)." self.ui.optionWidget.setCurrentIndex(7) # # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) else: try: l, h, w = value.split(";") self.ui.morse_low.setValue(int(l)) self.ui.morse_high.setValue(int(h)) self.ui.morse_wait.setValue(int(w)) except ValueError: self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) else: self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) self.ui.morse_low.setEnabled(decoderEdit) self.ui.morse_high.setEnabled(decoderEdit) self.ui.morse_wait.setEnabled(decoderEdit) elif constants.DECODING_CARRIER in element: txt += "A carrier is a fixed pattern like 1_1_1_1 where the actual data lies in between, e.g. 1a1a1b1. This " \ "function extracts the actual bit information (here: aab) from the signal at '_'/'.' positions.\n" \ "Examples:\n" \ "- Carrier = '1_' means 1_1_1_...\n" \ "- Carrier = '01_' means 01_01_01_01..." self.ui.optionWidget.setCurrentIndex(2) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.carrier.setText("1_") else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.carrier.setText("1_") else: self.ui.carrier.setText(value) else: self.ui.carrier.setText("1_") self.ui.carrier.setEnabled(decoderEdit) elif constants.DECODING_DATAWHITENING in element: txt += "Texas Instruments CC110x chips allow a data whitening that is applied before sending the signals to HF. " \ "After a preamble (1010...) there is a fixed 16/32 bit sync word. The following data (incl. 16 bit CRC) " \ "is masked (XOR) with the output of a LFSR.\n" \ "This unmasks the data." self.ui.optionWidget.setCurrentIndex(5) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_overwrite_crc.setChecked(False) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_overwrite_crc.setChecked(False) else: try: whitening_sync, whitening_polynomial, whitening_overwrite_crc = value.split(";") self.ui.datawhitening_sync.setText(whitening_sync) self.ui.datawhitening_polynomial.setText(whitening_polynomial) self.ui.datawhitening_overwrite_crc.setChecked(True if whitening_overwrite_crc == "1" else False) except ValueError: self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_overwrite_crc.setChecked(False) self.ui.datawhitening_sync.setEnabled(decoderEdit) self.ui.datawhitening_polynomial.setEnabled(decoderEdit) self.ui.datawhitening_overwrite_crc.setEnabled(decoderEdit) elif constants.DECODING_CUT in element: txt += "This function enables you to cut data from your messages, in order to shorten or align them for a " \ "better view. Note that this decoding does NOT support encoding, because cut data is gone!\n" \ "Example:\n" \ "- Cut before '1010' would delete everything before first '1010' bits.\n" \ "- Cut before Position = 3 (in bit) would delete the first three bits.\n" self.ui.optionWidget.setCurrentIndex(6) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.cutmark.setText("1010") self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.rB_delbefore.setChecked(False) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(False) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.cutmark.setText("1010") self.ui.cutmark.setEnabled(True) self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.cutmark2.setEnabled(False) self.ui.rB_delbefore.setChecked(True) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) else: try: cmode, cmark = value.split(";") cmode = int(cmode) if cmode == 0: self.ui.rB_delbefore.setChecked(True) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) self.ui.cutmark.setText(cmark) elif cmode == 1: self.ui.rB_delafter.setChecked(True) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) self.ui.cutmark.setText(cmark) elif cmode == 2: self.ui.rB_delbeforepos.setChecked(True) self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(True) self.ui.cutmark2.setValue(int(cmark)) elif cmode == 3: self.ui.rB_delafterpos.setChecked(True) self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(True) self.ui.cutmark2.setValue(int(cmark)) except ValueError: self.ui.cutmark.setText("1010") self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.rB_delbefore.setChecked(True) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) else: self.ui.cutmark.setText("1010") self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.rB_delbefore.setChecked(True) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) self.ui.rB_delbefore.setEnabled(decoderEdit) self.ui.rB_delafter.setEnabled(decoderEdit) self.ui.rB_delbeforepos.setEnabled(decoderEdit) self.ui.rB_delafterpos.setEnabled(decoderEdit) self.ui.info.setText(txt) @pyqtSlot() def handle_datawhitening(self): datawhiteningstr = self.ui.datawhitening_sync.text() + ";" + self.ui.datawhitening_polynomial.text() + ";" + \ ("1" if self.ui.datawhitening_overwrite_crc.isChecked() else "0") if constants.DECODING_DATAWHITENING in self.active_message: self.chainoptions[self.active_message] = datawhiteningstr self.decoderchainUpdate() @pyqtSlot() def handle_external(self): externalstr = self.ui.external_decoder.text() + ";" + self.ui.external_encoder.text() if constants.DECODING_EXTERNAL in self.active_message: self.chainoptions[self.active_message] = externalstr self.decoderchainUpdate() @pyqtSlot() def handle_substitution_changed(self): subststr = "" for i in range(0, self.ui.substitution_rows.value()): if self.ui.substitution.item(i, 0) and self.ui.substitution.item(i, 1): subststr += self.ui.substitution.item(i, 0).text() + ":" + self.ui.substitution.item(i, 1).text() + ";" if constants.DECODING_SUBSTITUTION in self.active_message: self.chainoptions[self.active_message] = subststr self.decoderchainUpdate() @pyqtSlot() def handle_substitution_rows_changed(self): # Substitution Row Spinbox self.ui.substitution.setRowCount(self.ui.substitution_rows.value()) self.decoderchainUpdate() @pyqtSlot() def handle_multiple_changed(self): # Multiple Spinbox val = self.ui.multiple.value() if constants.DECODING_REDUNDANCY in self.active_message: self.chainoptions[self.active_message] = val self.decoderchainUpdate() @pyqtSlot() def handle_morse_changed(self): # Multiple Spinbox val_low = self.ui.morse_low.value() val_high = self.ui.morse_high.value() val_wait = self.ui.morse_wait.value() if val_low >= val_high: self.ui.morse_low.setValue(self.old_morse[0]) self.ui.morse_high.setValue(self.old_morse[1]) (val_low, val_high) = self.old_morse else: self.old_morse = (val_low, val_high) if constants.DECODING_MORSE in self.active_message: self.chainoptions[self.active_message] = "{};{};{}".format(val_low, val_high, val_wait) self.decoderchainUpdate() @pyqtSlot() def handle_carrier_changed(self): # Only allow {0, 1} carrier_txt = self.ui.carrier.text() if carrier_txt.count("0") + carrier_txt.count("1") + carrier_txt.count("_") + carrier_txt.count( ".") + carrier_txt.count("*") < len(carrier_txt): self.ui.carrier.setText(self.old_carrier_txt) else: self.old_carrier_txt = carrier_txt # Carrier Textbox # self.e.carrier = self.e.str2bit(self.ui.carrier.text()) if constants.DECODING_CARRIER in self.active_message: self.chainoptions[self.active_message] = carrier_txt self.decoderchainUpdate() @pyqtSlot() def handle_cut(self): cmode = 0 cmark = "" if self.ui.rB_delbefore.isChecked() or self.ui.rB_delafter.isChecked(): # Activate right cutmark field self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) # set cmode if self.ui.rB_delafter.isChecked(): cmode = 1 # check values in cutmark cmark = self.ui.cutmark.text() if cmark.count("0") + cmark.count("1") < len(cmark): self.ui.cutmark.setText(self.old_cutmark) else: self.old_cutmark = cmark else: # Activate right cutmark field self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(True) # set cmode if self.ui.rB_delbeforepos.isChecked(): cmode = 2 else: cmode = 3 cmark = str(self.ui.cutmark2.value()) cut_text = str(cmode) + ";" + cmark if constants.DECODING_CUT in self.active_message: self.chainoptions[self.active_message] = cut_text self.decoderchainUpdate() def dragEnterEvent(self, event: QDragEnterEvent): event.accept() def dropEvent(self, event: QDropEvent): # if not self.ui.decoderchain.geometry().contains(self.mapToGlobal(event.pos())): if self.ui.decoderchain.currentItem() is not None: self.chainoptions.pop(self.ui.decoderchain.currentItem().text(), None) self.ui.decoderchain.takeItem(self.ui.decoderchain.currentRow()) self.decoderchainUpdate() def set_signal(self): indx = self.ui.combobox_signals.currentIndex() if indx != 0: self.ui.inpt.setReadOnly(True) else: self.ui.inpt.setReadOnly(False) self.ui.inpt.setText("10010110") self.decoder_update() return self.setCursor(Qt.WaitCursor) signal = self.signals[indx - 1] pa = ProtocolAnalyzer(signal) pa.get_protocol_from_signal() self.ui.inpt.setText("".join(pa.plain_bits_str)) self.ui.inpt.setCursorPosition(0) if signal is not None and pa.messages: last_message = pa.messages[-1] lookup = {i: msg.bit_sample_pos for i, msg in enumerate(pa.messages)} plot_data = signal.qad[lookup[0][0]:lookup[pa.num_messages - 1][len(last_message) - 1]] self.ui.graphicsView_signal.plot_data(plot_data) self.ui.graphicsView_signal.centerOn(0, 0) self.unsetCursor()
class DecoderWidgetController(QDialog): def __init__(self, decodings, signals, project_manager: ProjectManager, parent=None): """ :type decodings: list of Encoding :type signals: list of Signal """ # Init super().__init__(parent) self.ui = Ui_Decoder() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) # Variables self.old_inpt_txt = "" self.old_carrier_txt = "" self.old_decoderchain = [] self.active_message = "" self.old_cutmark = "" self.old_morse = (1, 3) self.project_manager = project_manager # Initialize encoder self.decodings = decodings self.ui.combobox_decodings.clear() for decoding in self.decodings: self.ui.combobox_decodings.addItem(decoding.name) self.chainstr = [] self.chainoptions = {} self.set_e() # Signals self.signals = signals if signals is not None else [] for signal in signals: if signal: self.ui.combobox_signals.addItem(signal.name) # Function lists self.ui.basefunctions.addItem(constants.DECODING_EDGE) self.ui.basefunctions.addItem(constants.DECODING_MORSE) self.ui.basefunctions.addItem(constants.DECODING_SUBSTITUTION) self.ui.basefunctions.addItem(constants.DECODING_EXTERNAL) self.ui.additionalfunctions.addItem(constants.DECODING_INVERT) self.ui.additionalfunctions.addItem(constants.DECODING_DIFFERENTIAL) self.ui.additionalfunctions.addItem(constants.DECODING_BITORDER) self.ui.additionalfunctions.addItem(constants.DECODING_REDUNDANCY) self.ui.additionalfunctions.addItem(constants.DECODING_CARRIER) self.ui.additionalfunctions.addItem(constants.DECODING_DATAWHITENING) self.ui.additionalfunctions.addItem(constants.DECODING_ENOCEAN) self.ui.additionalfunctions.addItem(constants.DECODING_CUT) # Presets self.setWindowTitle("Decoding") self.setWindowIcon(QIcon(":/icons/icons/decoding.svg")) self.setAcceptDrops(True) self.inpt_text = "10010110" self.ui.inpt.setText(self.inpt_text) self.ui.optionWidget.setCurrentIndex(0) self.decoder_update() self.ui.substitution.setColumnCount(2) self.ui.substitution.setRowCount(self.ui.substitution_rows.value()) self.ui.substitution.setHorizontalHeaderLabels(['From', 'To']) self.ui.substitution.setColumnWidth(0, 190) self.ui.substitution.setColumnWidth(1, 190) self.ui.saveas.setVisible(False) # Connects self.create_connects() try: self.restoreGeometry( constants.SETTINGS.value("{}/geometry".format( self.__class__.__name__))) except TypeError: pass def create_connects(self): self.ui.inpt.textChanged.connect(self.decoder_update) self.ui.multiple.valueChanged.connect(self.handle_multiple_changed) self.ui.carrier.textChanged.connect(self.handle_carrier_changed) self.ui.substitution_rows.valueChanged.connect( self.handle_substitution_rows_changed) self.ui.substitution.itemChanged.connect( self.handle_substitution_changed) self.ui.btnChooseDecoder.clicked.connect(self.choose_decoder) self.ui.btnChooseEncoder.clicked.connect(self.choose_encoder) self.ui.external_decoder.textEdited.connect(self.handle_external) self.ui.external_encoder.textEdited.connect(self.handle_external) self.ui.datawhitening_sync.textEdited.connect( self.handle_datawhitening) self.ui.datawhitening_polynomial.textEdited.connect( self.handle_datawhitening) self.ui.datawhitening_overwrite_crc.clicked.connect( self.handle_datawhitening) self.ui.decoderchain.itemChanged.connect(self.decoderchainUpdate) self.ui.decoderchain.internalMove.connect(self.decoderchainUpdate) self.ui.decoderchain.deleteElement.connect(self.deleteElement) self.ui.decoderchain.currentRowChanged.connect( self.on_decoder_chain_current_row_changed) self.ui.basefunctions.currentRowChanged.connect( self.on_base_functions_current_row_changed) self.ui.additionalfunctions.currentRowChanged.connect( self.on_additional_functions_current_row_changed) self.ui.combobox_decodings.currentIndexChanged.connect(self.set_e) self.ui.combobox_signals.currentIndexChanged.connect(self.set_signal) self.ui.saveas.clicked.connect(self.saveas) self.ui.delete_decoding.clicked.connect(self.delete_decoding) self.ui.rB_delbefore.clicked.connect(self.handle_cut) self.ui.rB_delafter.clicked.connect(self.handle_cut) self.ui.rB_delbeforepos.clicked.connect(self.handle_cut) self.ui.rB_delafterpos.clicked.connect(self.handle_cut) self.ui.cutmark.textEdited.connect(self.handle_cut) self.ui.cutmark2.valueChanged.connect(self.handle_cut) self.ui.morse_low.valueChanged.connect(self.handle_morse_changed) self.ui.morse_high.valueChanged.connect(self.handle_morse_changed) self.ui.morse_wait.valueChanged.connect(self.handle_morse_changed) def closeEvent(self, event: QCloseEvent): constants.SETTINGS.setValue( "{}/geometry".format(self.__class__.__name__), self.saveGeometry()) super().closeEvent(event) def choose_decoder(self): f, ok = QFileDialog.getOpenFileName(self, self.tr("Choose decoder program"), QDir.homePath()) if f and ok: self.ui.external_decoder.setText(f) self.handle_external() def choose_encoder(self): f, ok = QFileDialog.getOpenFileName(self, self.tr("Choose encoder program"), QDir.homePath()) if f and ok: self.ui.external_encoder.setText(f) self.handle_external() def save_to_file(self): if self.project_manager.project_file: prefix = os.path.realpath( os.path.dirname(self.project_manager.project_file)) else: prefix = os.path.realpath( os.path.join(constants.SETTINGS.fileName(), "..")) f = open(os.path.join(prefix, constants.DECODINGS_FILE), "w") if not f: return for i in range(0, self.ui.combobox_decodings.count()): str = "" for j in self.decodings[i].get_chain(): str += repr(j) + ", " str += "\n" f.write(str) f.close() def saveas(self): # Ask for a name name, ok = QInputDialog.getText(self, self.tr("Save decoding"), self.tr("Please enter a name:"), QLineEdit.Normal, self.e.chain[0]) if ok and name != "": self.e.chain[0] = name self.decoderchainUpdate() # If name is already there, overwrite existing for i in range(0, len(self.decodings)): if name == self.decodings[i].name: self.ui.combobox_decodings.setCurrentIndex(i) self.decodings[i] = Encoding(self.chainstr) self.set_e() self.ui.saveas.setVisible(False) self.save_to_file() return self.decodings.append(Encoding(self.chainstr)) self.ui.combobox_decodings.addItem(self.chainstr[0]) self.ui.combobox_decodings.setCurrentIndex( self.ui.combobox_decodings.count() - 1) self.set_e() self.save_to_file() def delete_decoding(self): num = self.ui.combobox_decodings.currentIndex() if num >= 0: reply = QMessageBox.question( self, self.tr("Delete Decoding?"), self.tr("Do you really want to delete " + "'{}'?".format(self.decodings[num].name)), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.decodings.pop(num) self.ui.combobox_decodings.removeItem(num) self.save_to_file() def set_e(self): if self.ui.combobox_decodings.count() < 1: # Empty list return self.e = copy.deepcopy( self.decodings[self.ui.combobox_decodings.currentIndex()]) """:type: encoding """ chain = self.e.get_chain() self.ui.decoderchain.clear() self.chainoptions.clear() last_i = "" for i in chain: if i in [ constants.DECODING_INVERT, constants.DECODING_ENOCEAN, constants.DECODING_DIFFERENTIAL, constants.DECODING_REDUNDANCY, constants.DECODING_CARRIER, constants.DECODING_BITORDER, constants.DECODING_EDGE, constants.DECODING_DATAWHITENING, constants.DECODING_SUBSTITUTION, constants.DECODING_EXTERNAL, constants.DECODING_CUT, constants.DECODING_MORSE, constants.DECODING_DISABLED_PREFIX ]: self.ui.decoderchain.addItem(i) self.decoderchainUpdate() last_i = self.ui.decoderchain.item( self.ui.decoderchain.count() - 1).text() else: if any(x in last_i for x in [ constants.DECODING_REDUNDANCY, constants. DECODING_CARRIER, constants.DECODING_SUBSTITUTION, constants.DECODING_EXTERNAL, constants.DECODING_DATAWHITENING, constants.DECODING_CUT, constants.DECODING_MORSE ]): self.chainoptions[last_i] = i self.decoderchainUpdate() self.decoder_update() self.ui.saveas.setVisible(False) def decoderchainUpdate(self): # for i in range (0, self.ui.decoderchain.count()): # print(i, "->", self.ui.decoderchain.item(i).text()) # print() self.ui.saveas.setVisible(True) self.eliminateDuplicates() self.chainstr = [self.e.name] for i in range(0, self.ui.decoderchain.count()): op = self.ui.decoderchain.item(i).text() # Is this function disabled? if constants.DECODING_DISABLED_PREFIX in op: continue self.chainstr.append(op) # Add parameters to chainstr if constants.DECODING_REDUNDANCY in op: # Read Multiple Value if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = 2 self.chainstr.append(2) # Default elif constants.DECODING_CARRIER in op: # Read Carrier Field and add string to chainstr if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_SUBSTITUTION in op: # Add substitution string to chainstr: Format = src0:dst0;src1:dst1;... if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_EXTERNAL in op: # Add program path's string to chainstr: Format = decoder;encoder if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_DATAWHITENING in op: # Add Data Whitening Parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("0xe9cae9ca;0x21;0") # Default elif constants.DECODING_CUT in op: # Add cut parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("0;1010") # Default elif constants.DECODING_MORSE in op: # Add morse parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("1;3;1") # Default self.e.set_chain(self.chainstr) self.decoder_update() def deleteElement(self): if self.ui.decoderchain.count() == 0: # Clear all self.chainoptions.clear() else: self.chainoptions.pop(self.ui.decoderchain.active_element_text, None) self.decoderchainUpdate() def eliminateDuplicates(self): decoderchain_count = self.ui.decoderchain.count() olddecoderchain_count = len(self.old_decoderchain) # Special case for 1 element (add " ") if decoderchain_count == 1: tmp = self.ui.decoderchain.item(0).text() if tmp[-1] != " " and not tmp[-1].isnumeric(): self.ui.decoderchain.takeItem(0) self.ui.decoderchain.insertItem(0, tmp + " ") # Ignore internal move (same count()) and removed elements and lists < 2 // len(self.old_decoderchain)+1 == self.ui.decoderchain.count() if decoderchain_count > 1 and decoderchain_count > olddecoderchain_count: elem = 0 while elem < olddecoderchain_count: if self.ui.decoderchain.item( elem).text() == self.old_decoderchain[elem]: elem += 1 else: break # Count number of current elements and append string "#<num>" to current text, if num > 1 txt = self.ui.decoderchain.item(elem).text() num = 0 for i in range(0, decoderchain_count): if txt in self.ui.decoderchain.item(i).text(): num += 1 if num > 1: tmp_txt = txt + " #" + str(num) else: tmp_txt = txt + " " # Check duplicate names dup = False for i in range(0, decoderchain_count): if self.ui.decoderchain.item(i).text() == tmp_txt: dup = True break if dup: for i in range(1, num): if i > 1: tmp_txt = txt + " #" + str(i) else: tmp_txt = txt + " " dup = False for j in range(0, decoderchain_count): if self.ui.decoderchain.item(j).text() == tmp_txt: dup = True break if not dup: break # Replace current element with new "text #<num>" if not dup: self.ui.decoderchain.takeItem(elem) self.ui.decoderchain.insertItem(elem, tmp_txt) # Save current decoderchain to old_decoderchain self.old_decoderchain = [] for i in range(0, decoderchain_count): self.old_decoderchain.append(self.ui.decoderchain.item(i).text()) def decoder_update(self): # Only allow {0, 1} signaltype = self.ui.combobox_signals.currentIndex() inpt_txt = self.ui.inpt.text() if signaltype == 0: if inpt_txt.count("0") + inpt_txt.count("1") < len(inpt_txt): self.ui.inpt.setText(self.old_inpt_txt) else: self.old_inpt_txt = inpt_txt # Write decoded bits bit = self.e.str2bit(self.ui.inpt.text()) decoded = self.e.bit2str(self.e.decode(bit)) errors = "[Decoding Errors = " + str(self.e.analyze(bit)[0]) + "]" self.ui.decoding_errors_label.setText(errors) self.ui.output.setText(decoded) self.ui.output.setCursorPosition(0) if len(decoded) > 0: if signaltype == 0: temp_signal = SignalSceneManager.create_rectangle(inpt_txt)[0] self.ui.graphicsView_signal.setScene(temp_signal) self.ui.graphicsView_signal.update() temp_decoded = SignalSceneManager.create_rectangle(decoded)[0] self.ui.graphicsView_decoded.setScene(temp_decoded) self.ui.graphicsView_decoded.update() @pyqtSlot(int) def on_base_functions_current_row_changed(self, index: int): if self.ui.basefunctions.currentItem().text() is not None: self.ui.decoderchain.setCurrentRow(-1) self.set_information(0) else: self.ui.optionWidget.setCurrentIndex(0) self.ui.info.clear() @pyqtSlot(int) def on_additional_functions_current_row_changed(self, index: int): if self.ui.additionalfunctions.currentItem() is not None: self.ui.decoderchain.setCurrentRow(-1) self.set_information(1) else: self.ui.optionWidget.setCurrentIndex(0) self.ui.info.clear() @pyqtSlot(int) def on_decoder_chain_current_row_changed(self, index: int): if self.ui.decoderchain.currentItem() is not None: self.set_information(2) else: self.ui.optionWidget.setCurrentIndex(0) self.ui.info.clear() def set_information(self, mode: int): # Presets decoderEdit = False self.ui.optionWidget.setCurrentIndex(0) txt = "" # Determine selected element if mode == 0: element = self.ui.basefunctions.currentItem().text() txt += element + ":\n" elif mode == 1: element = self.ui.additionalfunctions.currentItem().text() txt += element + ":\n" elif mode == 2: decoderEdit = True txt = "## DECODING PROCESS ##\n\n" element = self.ui.decoderchain.currentItem().text() if element[-1] == " ": elementname = element[0:-1] else: elementname = element txt += elementname + ":\n" self.active_message = element # Remove "[Disabled] " for further tasks if constants.DECODING_DISABLED_PREFIX in element: element = element[len(constants.DECODING_DISABLED_PREFIX):] # Write info text and show options if constants.DECODING_EDGE in element: txt += "Trigger on signal edge, i.e. the transition between low and high.\n" \ "- Low to High (01) is 1\n" \ "- High to Low (10) is 0" elif constants.DECODING_SUBSTITUTION in element: txt += "A set of manual defined signal sequences FROM (e.g. 110, 100) is replaced by another set of " \ "sequences TO (e.g. 01, 10). Note that all FROM entries must have the same length, otherwise " \ "the result is unpredictable! (For TX: all TO entries must have the same length)" self.ui.optionWidget.setCurrentIndex(3) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) else: if element in self.chainoptions: values = self.chainoptions[element] if values == "": self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) else: arrs = self.e.get_subst_array(values) if len(arrs[0]) == len(arrs[1]): self.ui.substitution_rows.setValue(len(arrs[0])) self.ui.substitution.setRowCount(len(arrs[0])) for i in range(0, len(arrs[0])): self.ui.substitution.setItem( i, 0, QTableWidgetItem(self.e.bit2str( arrs[0][i]))) self.ui.substitution.setItem( i, 1, QTableWidgetItem(self.e.bit2str( arrs[1][i]))) else: self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) self.ui.substitution.setEnabled(decoderEdit) self.ui.substitution_rows.setEnabled(decoderEdit) elif constants.DECODING_EXTERNAL in element: txt += "The decoding (and encoding) process is delegated to external programs or scripts via parameter.\n" \ "Example: Given the signal 10010110, your program is called as './decoder 10010110'. Your program " \ "computes and prints a corresponding set of 0s and 1s which is fed back into the decoding process. " self.ui.optionWidget.setCurrentIndex(4) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") else: decstr, encstr = value.split(";") self.ui.external_decoder.setText(decstr) self.ui.external_encoder.setText(encstr) else: self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") self.ui.external_decoder.setEnabled(decoderEdit) self.ui.external_encoder.setEnabled(decoderEdit) elif constants.DECODING_INVERT in element: txt += "All bits are inverted, i.e. 0->1 and 1->0." elif constants.DECODING_ENOCEAN in element: txt += "Remove Wireless Short-Packet (WSP) encoding that is used by EnOcean standard." elif constants.DECODING_DIFFERENTIAL in element: txt += "Every transition between low and high (0->1 or 1->0) becomes 1, no transition (0->0 or 1->1) remains 0.\n" \ "The first signal bit is regarded as start value and directly copied.\n" \ "Example: 0011 becomes 0010 [0|(0->0)|(0->1)|(1->1)]." elif constants.DECODING_BITORDER in element: txt += "Every byte (8 bit) is reversed, i.e. the order of the bits 01234567 (e.g. least significant bit first) " \ "is changed to 76543210 (e.g. most significant bit first)." elif constants.DECODING_REDUNDANCY in element: txt += "If the source signal always has multiple redundant bits for one bit (e.g. 1111=1, 0000=0), the " \ "redundancy is removed here. You have to define the number of redundant bits." self.ui.optionWidget.setCurrentIndex(1) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.multiple.setValue(2) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.multiple.setValue(2) else: self.ui.multiple.setValue(int(value)) else: self.ui.multiple.setValue(2) self.ui.multiple.setEnabled(decoderEdit) elif constants.DECODING_MORSE in element: txt += "If the signal is a morse code, e.g. 00111001001110011100, where information are " \ "transported with long and short sequences of 1 (0 just for padding), then this " \ "decoding evaluates those sequences (Example output: 1011)." self.ui.optionWidget.setCurrentIndex(7) # # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) else: try: l, h, w = value.split(";") self.ui.morse_low.setValue(int(l)) self.ui.morse_high.setValue(int(h)) self.ui.morse_wait.setValue(int(w)) except ValueError: self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) else: self.ui.morse_low.setValue(1) self.ui.morse_high.setValue(3) self.ui.morse_wait.setValue(1) self.ui.morse_low.setEnabled(decoderEdit) self.ui.morse_high.setEnabled(decoderEdit) self.ui.morse_wait.setEnabled(decoderEdit) elif constants.DECODING_CARRIER in element: txt += "A carrier is a fixed pattern like 1_1_1_1 where the actual data lies in between, e.g. 1a1a1b1. This " \ "function extracts the actual bit information (here: aab) from the signal at '_'/'.' positions.\n" \ "Examples:\n" \ "- Carrier = '1_' means 1_1_1_...\n" \ "- Carrier = '01_' means 01_01_01_01..." self.ui.optionWidget.setCurrentIndex(2) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.carrier.setText("1_") else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.carrier.setText("1_") else: self.ui.carrier.setText(value) else: self.ui.carrier.setText("1_") self.ui.carrier.setEnabled(decoderEdit) elif constants.DECODING_DATAWHITENING in element: txt += "Texas Instruments CC110x chips allow a data whitening that is applied before sending the signals to HF. " \ "After a preamble (1010...) there is a fixed 16/32 bit sync word. The following data (incl. 16 bit CRC) " \ "is masked (XOR) with the output of a LFSR.\n" \ "This unmasks the data." self.ui.optionWidget.setCurrentIndex(5) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_overwrite_crc.setChecked(False) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_overwrite_crc.setChecked(False) else: try: whitening_sync, whitening_polynomial, whitening_overwrite_crc = value.split( ";") self.ui.datawhitening_sync.setText(whitening_sync) self.ui.datawhitening_polynomial.setText( whitening_polynomial) self.ui.datawhitening_overwrite_crc.setChecked( True if whitening_overwrite_crc == "1" else False) except ValueError: self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_overwrite_crc.setChecked( False) self.ui.datawhitening_sync.setEnabled(decoderEdit) self.ui.datawhitening_polynomial.setEnabled(decoderEdit) self.ui.datawhitening_overwrite_crc.setEnabled(decoderEdit) elif constants.DECODING_CUT in element: txt += "This function enables you to cut data from your messages, in order to shorten or align them for a " \ "better view. Note that this decoding does NOT support encoding, because cut data is gone!\n" \ "Example:\n" \ "- Cut before '1010' would delete everything before first '1010' bits.\n" \ "- Cut before Position = 3 (in bit) would delete the first three bits.\n" self.ui.optionWidget.setCurrentIndex(6) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.cutmark.setText("1010") self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.rB_delbefore.setChecked(False) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(False) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.cutmark.setText("1010") self.ui.cutmark.setEnabled(True) self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.cutmark2.setEnabled(False) self.ui.rB_delbefore.setChecked(True) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) else: try: cmode, cmark = value.split(";") cmode = int(cmode) if cmode == 0: self.ui.rB_delbefore.setChecked(True) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) self.ui.cutmark.setText(cmark) elif cmode == 1: self.ui.rB_delafter.setChecked(True) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) self.ui.cutmark.setText(cmark) elif cmode == 2: self.ui.rB_delbeforepos.setChecked(True) self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(True) self.ui.cutmark2.setValue(int(cmark)) elif cmode == 3: self.ui.rB_delafterpos.setChecked(True) self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(True) self.ui.cutmark2.setValue(int(cmark)) except ValueError: self.ui.cutmark.setText("1010") self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.rB_delbefore.setChecked(True) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) else: self.ui.cutmark.setText("1010") self.old_cutmark = self.ui.cutmark.text() self.ui.cutmark2.setValue(1) self.ui.rB_delbefore.setChecked(True) self.ui.rB_delafter.setChecked(False) self.ui.rB_delbeforepos.setChecked(False) self.ui.rB_delafterpos.setChecked(False) self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) self.ui.rB_delbefore.setEnabled(decoderEdit) self.ui.rB_delafter.setEnabled(decoderEdit) self.ui.rB_delbeforepos.setEnabled(decoderEdit) self.ui.rB_delafterpos.setEnabled(decoderEdit) self.ui.info.setText(txt) @pyqtSlot() def handle_datawhitening(self): datawhiteningstr = self.ui.datawhitening_sync.text() + ";" + self.ui.datawhitening_polynomial.text() + ";" + \ ("1" if self.ui.datawhitening_overwrite_crc.isChecked() else "0") if constants.DECODING_DATAWHITENING in self.active_message: self.chainoptions[self.active_message] = datawhiteningstr self.decoderchainUpdate() @pyqtSlot() def handle_external(self): externalstr = self.ui.external_decoder.text( ) + ";" + self.ui.external_encoder.text() if constants.DECODING_EXTERNAL in self.active_message: self.chainoptions[self.active_message] = externalstr self.decoderchainUpdate() @pyqtSlot() def handle_substitution_changed(self): subststr = "" for i in range(0, self.ui.substitution_rows.value()): if self.ui.substitution.item(i, 0) and self.ui.substitution.item( i, 1): subststr += self.ui.substitution.item( i, 0).text() + ":" + self.ui.substitution.item( i, 1).text() + ";" if constants.DECODING_SUBSTITUTION in self.active_message: self.chainoptions[self.active_message] = subststr self.decoderchainUpdate() @pyqtSlot() def handle_substitution_rows_changed(self): # Substitution Row Spinbox self.ui.substitution.setRowCount(self.ui.substitution_rows.value()) self.decoderchainUpdate() @pyqtSlot() def handle_multiple_changed(self): # Multiple Spinbox val = self.ui.multiple.value() if constants.DECODING_REDUNDANCY in self.active_message: self.chainoptions[self.active_message] = val self.decoderchainUpdate() @pyqtSlot() def handle_morse_changed(self): # Multiple Spinbox val_low = self.ui.morse_low.value() val_high = self.ui.morse_high.value() val_wait = self.ui.morse_wait.value() if val_low >= val_high: self.ui.morse_low.setValue(self.old_morse[0]) self.ui.morse_high.setValue(self.old_morse[1]) (val_low, val_high) = self.old_morse else: self.old_morse = (val_low, val_high) if constants.DECODING_MORSE in self.active_message: self.chainoptions[self.active_message] = "{};{};{}".format( val_low, val_high, val_wait) self.decoderchainUpdate() @pyqtSlot() def handle_carrier_changed(self): # Only allow {0, 1} carrier_txt = self.ui.carrier.text() if carrier_txt.count("0") + carrier_txt.count("1") + carrier_txt.count( "_") + carrier_txt.count(".") + carrier_txt.count("*") < len( carrier_txt): self.ui.carrier.setText(self.old_carrier_txt) else: self.old_carrier_txt = carrier_txt # Carrier Textbox # self.e.carrier = self.e.str2bit(self.ui.carrier.text()) if constants.DECODING_CARRIER in self.active_message: self.chainoptions[self.active_message] = carrier_txt self.decoderchainUpdate() @pyqtSlot() def handle_cut(self): cmode = 0 cmark = "" if self.ui.rB_delbefore.isChecked() or self.ui.rB_delafter.isChecked(): # Activate right cutmark field self.ui.cutmark.setEnabled(True) self.ui.cutmark2.setEnabled(False) # set cmode if self.ui.rB_delafter.isChecked(): cmode = 1 # check values in cutmark cmark = self.ui.cutmark.text() if cmark.count("0") + cmark.count("1") < len(cmark): self.ui.cutmark.setText(self.old_cutmark) else: self.old_cutmark = cmark else: # Activate right cutmark field self.ui.cutmark.setEnabled(False) self.ui.cutmark2.setEnabled(True) # set cmode if self.ui.rB_delbeforepos.isChecked(): cmode = 2 else: cmode = 3 cmark = str(self.ui.cutmark2.value()) cut_text = str(cmode) + ";" + cmark if constants.DECODING_CUT in self.active_message: self.chainoptions[self.active_message] = cut_text self.decoderchainUpdate() def dragEnterEvent(self, event: QDragEnterEvent): event.accept() def dropEvent(self, event: QDropEvent): # if not self.ui.decoderchain.geometry().contains(self.mapToGlobal(event.pos())): if self.ui.decoderchain.currentItem() is not None: self.chainoptions.pop(self.ui.decoderchain.currentItem().text(), None) self.ui.decoderchain.takeItem(self.ui.decoderchain.currentRow()) self.decoderchainUpdate() def set_signal(self): indx = self.ui.combobox_signals.currentIndex() if indx != 0: self.ui.inpt.setReadOnly(True) else: self.ui.inpt.setReadOnly(False) self.ui.inpt.setText("10010110") self.decoder_update() return self.setCursor(Qt.WaitCursor) signal = self.signals[indx - 1] pa = ProtocolAnalyzer(signal) pa.get_protocol_from_signal() self.ui.inpt.setText("".join(pa.plain_bits_str)) self.ui.inpt.setCursorPosition(0) if signal is not None and pa.messages: last_message = pa.messages[-1] lookup = { i: msg.bit_sample_pos for i, msg in enumerate(pa.messages) } plot_data = signal.qad[lookup[0][0]:lookup[pa.num_messages - 1][len(last_message) - 1]] self.ui.graphicsView_signal.plot_data(plot_data) self.ui.graphicsView_signal.centerOn(0, 0) self.unsetCursor()
class DecoderWidgetController(QDialog): def __init__(self, decodings, signals, project_manager: ProjectManager, parent=None): """ :type decodings: list of encoding :type signals: list of Signal """ # Init super().__init__(parent) self.ui = Ui_Decoder() self.ui.setupUi(self) # Variables self.old_inpt_txt = "" self.old_carrier_txt = "" self.old_decoderchain = [] self.active_block = "" self.project_manager = project_manager # Initialize encoder self.decodings = decodings self.ui.combobox_decodings.clear() for decoding in self.decodings: self.ui.combobox_decodings.addItem(decoding.name) self.chainstr = [] self.chainoptions = {} self.set_e() # Signals self.signals = signals if signals is not None else [] for i in signals: self.ui.combobox_signals.addItem(i.name) # Function lists self.ui.basefunctions.addItem(constants.DECODING_EDGE) self.ui.basefunctions.addItem(constants.DECODING_SUBSTITUTION) self.ui.basefunctions.addItem(constants.DECODING_EXTERNAL) self.ui.additionalfunctions.addItem(constants.DECODING_INVERT) self.ui.additionalfunctions.addItem(constants.DECODING_DIFFERENTIAL) self.ui.additionalfunctions.addItem(constants.DECODING_BITORDER) self.ui.additionalfunctions.addItem(constants.DECODING_REDUNDANCY) self.ui.additionalfunctions.addItem(constants.DECODING_CARRIER) self.ui.additionalfunctions.addItem(constants.DECODING_DATAWHITENING) # Presets self.setWindowTitle("Decoding") self.setAcceptDrops(True) self.inpt_text = "10010110" self.ui.inpt.setText(self.inpt_text) self.ui.optionWidget.setCurrentIndex(0) self.decoder_update() self.ui.substitution.setColumnCount(2) self.ui.substitution.setRowCount(self.ui.substitution_rows.value()) self.ui.substitution.setHorizontalHeaderLabels(['From', 'To']) self.ui.substitution.setColumnWidth(0, 190) self.ui.substitution.setColumnWidth(1, 190) self.ui.saveas.setVisible(False) # Connects self.create_connects() def create_connects(self): self.ui.inpt.textChanged.connect(self.decoder_update) self.ui.multiple.valueChanged.connect(self.handle_multiple_changed) self.ui.carrier.textChanged.connect(self.handle_carrier_changed) self.ui.substitution_rows.valueChanged.connect( self.handle_substitution_rows_changed) self.ui.substitution.itemChanged.connect( self.handle_substitution_changed) self.ui.external_decoder.textEdited.connect(self.handle_external) self.ui.external_encoder.textEdited.connect(self.handle_external) self.ui.datawhitening_sync.textEdited.connect( self.handle_datawhitening) self.ui.datawhitening_polynomial.textEdited.connect( self.handle_datawhitening) self.ui.datawhitening_applycrc.clicked.connect( self.handle_datawhitening) self.ui.datawhitening_preamble_rm.clicked.connect( self.handle_datawhitening) self.ui.datawhitening_sync_rm.clicked.connect( self.handle_datawhitening) self.ui.datawhitening_crc_rm.clicked.connect(self.handle_datawhitening) self.ui.decoderchain.itemChanged.connect(self.decoderchainUpdate) self.ui.decoderchain.internalMove.connect(self.decoderchainUpdate) self.ui.decoderchain.itemActivated.connect(self.setInformation) self.ui.decoderchain.deleteElement.connect(self.deleteElement) self.ui.basefunctions.itemActivated.connect(self.setInformation) self.ui.additionalfunctions.itemActivated.connect(self.setInformation) self.ui.combobox_decodings.currentIndexChanged.connect(self.set_e) self.ui.combobox_signals.currentIndexChanged.connect(self.set_signal) self.ui.saveas.clicked.connect(self.saveas) self.ui.delete_decoding.clicked.connect(self.delete_decoding) def save_to_file(self): if self.project_manager.project_file: prefix = os.path.realpath( os.path.dirname(self.project_manager.project_file)) else: prefix = os.path.realpath( os.path.join(constants.SETTINGS.fileName(), "..")) f = open(os.path.join(prefix, constants.DECODINGS_FILE), "w") if not f: return for i in range(0, self.ui.combobox_decodings.count()): str = "" for j in self.decodings[i].get_chain(): str += repr(j) + ", " str += "\n" f.write(str) f.close() def saveas(self): # Ask for a name txt = ["Please enter a name:", self.e.chain[0]] ok, name = CustomDialog.dialog(self, txt, "input") if ok and name != "": self.e.chain[0] = name self.decoderchainUpdate() # If name is already there, overwrite existing for i in range(0, len(self.decodings)): if name == self.decodings[i].name: self.ui.combobox_decodings.setCurrentIndex(i) self.decodings[i] = encoding(self.chainstr) self.set_e() self.ui.saveas.setVisible(False) self.save_to_file() return self.decodings.append(encoding(self.chainstr)) self.ui.combobox_decodings.addItem(self.chainstr[0]) self.ui.combobox_decodings.setCurrentIndex( self.ui.combobox_decodings.count() - 1) self.set_e() self.save_to_file() def delete_decoding(self): num = self.ui.combobox_decodings.currentIndex() if num >= 0: # Ask for acknowledgement txt = "Do you really want to delete '" + self.decodings[ num].name + "'?" ok, _ = CustomDialog.dialog(self, txt, "yesno") if ok: self.decodings.pop(num) self.ui.combobox_decodings.removeItem(num) self.save_to_file() def set_e(self): if self.ui.combobox_decodings.count() < 1: # Empty list return self.e = copy.deepcopy( self.decodings[self.ui.combobox_decodings.currentIndex()]) """:type: encoding """ chain = self.e.get_chain() self.ui.decoderchain.clear() self.chainoptions.clear() last_i = "" for i in chain: if i in [ constants.DECODING_INVERT, constants.DECODING_DIFFERENTIAL, constants.DECODING_REDUNDANCY, constants.DECODING_CARRIER, constants.DECODING_BITORDER, constants.DECODING_EDGE, constants.DECODING_DATAWHITENING, constants.DECODING_SUBSTITUTION, constants.DECODING_EXTERNAL, constants.DECODING_DISABLED_PREFIX ]: self.ui.decoderchain.addItem(i) self.decoderchainUpdate() last_i = self.ui.decoderchain.item( self.ui.decoderchain.count() - 1).text() else: if any(x in last_i for x in [ constants.DECODING_REDUNDANCY, constants. DECODING_CARRIER, constants.DECODING_SUBSTITUTION, constants.DECODING_EXTERNAL, constants.DECODING_DATAWHITENING ]): self.chainoptions[last_i] = i self.decoderchainUpdate() self.decoder_update() self.ui.saveas.setVisible(False) def decoderchainUpdate(self): # for i in range (0, self.ui.decoderchain.count()): # print(i, "->", self.ui.decoderchain.item(i).text()) # print() self.ui.saveas.setVisible(True) self.eliminateDuplicates() self.chainstr = [self.e.name] for i in range(0, self.ui.decoderchain.count()): op = self.ui.decoderchain.item(i).text() # Is this function disabled? if constants.DECODING_DISABLED_PREFIX in op: continue self.chainstr.append(op) # Add parameters to chainstr if constants.DECODING_REDUNDANCY in op: # Read Multiple Value if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = 2 self.chainstr.append(2) # Default elif constants.DECODING_CARRIER in op: # Read Carrier Field and add string to chainstr if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_SUBSTITUTION in op: # Add substitution string to chainstr: Format = src0:dst0;src1:dst1;... if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_EXTERNAL in op: # Add program path's string to chainstr: Format = decoder;encoder if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("") # Default elif constants.DECODING_DATAWHITENING in op: # Add Data Whitening Parameters if op in self.chainoptions: self.chainstr.append(self.chainoptions[op]) else: self.chainoptions[op] = "" self.chainstr.append("0xe9cae9ca;0x21;0xe") # Default self.e.set_chain(self.chainstr) self.decoder_update() def deleteElement(self): if self.ui.decoderchain.count() == 0: # Clear all self.chainoptions.clear() else: self.chainoptions.pop(self.ui.decoderchain.active_element_text, None) self.decoderchainUpdate() def eliminateDuplicates(self): decoderchain_count = self.ui.decoderchain.count() olddecoderchain_count = len(self.old_decoderchain) # Special case for 1 element (add " ") if decoderchain_count == 1: tmp = self.ui.decoderchain.item(0).text() if tmp[-1] != " " and not tmp[-1].isnumeric(): self.ui.decoderchain.takeItem(0) self.ui.decoderchain.insertItem(0, tmp + " ") # Ignore internal move (same count()) and removed elements and lists < 2 // len(self.old_decoderchain)+1 == self.ui.decoderchain.count() if decoderchain_count > 1 and decoderchain_count > olddecoderchain_count: elem = 0 while elem < olddecoderchain_count: if self.ui.decoderchain.item( elem).text() == self.old_decoderchain[elem]: elem += 1 else: break # Count number of current elements and append string "#<num>" to current text, if num > 1 txt = self.ui.decoderchain.item(elem).text() num = 0 for i in range(0, decoderchain_count): if txt in self.ui.decoderchain.item(i).text(): num += 1 if num > 1: tmp_txt = txt + " #" + str(num) else: tmp_txt = txt + " " # Check duplicate names dup = False for i in range(0, decoderchain_count): if self.ui.decoderchain.item(i).text() == tmp_txt: dup = True break if dup: for i in range(1, num): if i > 1: tmp_txt = txt + " #" + str(i) else: tmp_txt = txt + " " dup = False for j in range(0, decoderchain_count): if self.ui.decoderchain.item(j).text() == tmp_txt: dup = True break if not dup: break # Replace current element with new "text #<num>" if not dup: self.ui.decoderchain.takeItem(elem) self.ui.decoderchain.insertItem(elem, tmp_txt) # Save current decoderchain to old_decoderchain self.old_decoderchain = [] for i in range(0, decoderchain_count): self.old_decoderchain.append(self.ui.decoderchain.item(i).text()) def decoder_update(self): # Only allow {0, 1} signaltype = self.ui.combobox_signals.currentIndex() inpt_txt = self.ui.inpt.text() if signaltype == 0: if inpt_txt.count("0") + inpt_txt.count("1") < len(inpt_txt): self.ui.inpt.setText(self.old_inpt_txt) else: self.old_inpt_txt = inpt_txt # Write decoded bits bit = self.e.str2bit(self.ui.inpt.text()) decoded = self.e.bit2str(self.e.decode(bit)) errors = "[Decoding Errors = " + str(self.e.analyze(bit)) + "]" self.ui.decoding_errors_label.setText(errors) self.ui.output.setText(decoded) if len(decoded) > 0: if signaltype == 0: temp_signal = SignalSceneManager.create_rectangle(inpt_txt)[0] self.ui.graphicsView_signal.setScene(temp_signal) self.ui.graphicsView_signal.update() temp_decoded = SignalSceneManager.create_rectangle(decoded)[0] self.ui.graphicsView_decoded.setScene(temp_decoded) self.ui.graphicsView_decoded.update() def setInformation(self): # Presets decoderEdit = False self.ui.optionWidget.setCurrentIndex(0) txt = "" # Determine selected element if self.ui.basefunctions.hasFocus(): element = self.ui.basefunctions.currentItem().text() txt += element + ":\n" elif self.ui.additionalfunctions.hasFocus(): element = self.ui.additionalfunctions.currentItem().text() txt += element + ":\n" elif self.ui.decoderchain.hasFocus(): decoderEdit = True txt = "## DECODING PROCESS ##\n\n" element = self.ui.decoderchain.currentItem().text() if element[-1] == " ": elementname = element[0:-1] else: elementname = element txt += elementname + ":\n" self.active_block = element # Remove "[Disabled] " for further tasks if constants.DECODING_DISABLED_PREFIX in element: element = element[len(constants.DECODING_DISABLED_PREFIX):] # Write info text and show options if constants.DECODING_EDGE in element: txt += "Trigger on signal edge, i.e. the transition between low and high.\n" \ "- Low to High (01) is 1\n" \ "- High to Low (10) is 0" elif constants.DECODING_SUBSTITUTION in element: txt += "A set of manual defined signal sequences FROM (e.g. 10, 01) is replaced by another set of " \ "sequences TO (e.g. 01, 10)." self.ui.optionWidget.setCurrentIndex(3) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) else: if element in self.chainoptions: values = self.chainoptions[element] if values == "": self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) else: arrs = self.e.get_subst_array(values) if len(arrs[0]) == len(arrs[1]): self.ui.substitution_rows.setValue(len(arrs[0])) self.ui.substitution.setRowCount(len(arrs[0])) for i in range(0, len(arrs[0])): self.ui.substitution.setItem( i, 0, QTableWidgetItem(self.e.bit2str( arrs[0][i]))) self.ui.substitution.setItem( i, 1, QTableWidgetItem(self.e.bit2str( arrs[1][i]))) else: self.ui.substitution_rows.setValue(4) self.ui.substitution.setRowCount(0) self.ui.substitution.setRowCount(4) self.ui.substitution.setEnabled(decoderEdit) self.ui.substitution_rows.setEnabled(decoderEdit) elif constants.DECODING_EXTERNAL in element: txt += "The decoding (and encoding) process is delegated to external programs or scripts via parameter.\n" \ "Example: Given the signal 10010110, your program is called as './decoder 10010110'. Your program " \ "computes and prints a corresponding set of 0s and 1s which is fed back into the decoding process. " self.ui.optionWidget.setCurrentIndex(4) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") else: decstr, encstr = value.split(";") self.ui.external_decoder.setText(decstr) self.ui.external_encoder.setText(encstr) else: self.ui.external_decoder.setText("") self.ui.external_encoder.setText("") self.ui.external_decoder.setEnabled(decoderEdit) self.ui.external_encoder.setEnabled(decoderEdit) elif constants.DECODING_INVERT in element: txt += "All bits are inverted, i.e. 0->1 and 1->0." elif constants.DECODING_DIFFERENTIAL in element: txt += "Every transition between low and high (0->1 or 1->0) becomes 1, no transition (0->0 or 1->1) remains 0.\n" \ "The first signal bit is regarded as start value and directly copied.\n" \ "Example: 0011 becomes 0010 [0|(0->0)|(0->1)|(1->1)]." elif constants.DECODING_BITORDER in element: txt += "Every byte (8 bit) is reversed, i.e. the order of the bits 01234567 (e.g. least significant bit first) " \ "is changed to 76543210 (e.g. most significant bit first)." elif constants.DECODING_REDUNDANCY in element: txt += "If the source signal always has multiple redundant bits for one bit (e.g. 1111=1, 0000=0), the " \ "redundancy is removed here. You have to define the number of redundant bits." self.ui.optionWidget.setCurrentIndex(1) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.multiple.setValue(2) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.multiple.setValue(2) else: self.ui.multiple.setValue(int(value)) else: self.ui.multiple.setValue(2) self.ui.multiple.setEnabled(decoderEdit) elif constants.DECODING_CARRIER in element: txt += "A carrier is a fixed pattern like 1_1_1_1 where the actual data lies in between, e.g. 1a1a1b1. This " \ "function extracts the actual bit information (here: aab) from the signal.\n" \ "Examples:\n" \ "- Carrier = '1' means 1_1_1_1...\n" \ "- Carrier = '01' means 01_01_01_01..." self.ui.optionWidget.setCurrentIndex(2) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.carrier.setText("1") else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.carrier.setText("1") else: self.ui.carrier.setText(value) else: self.ui.carrier.setText("1") self.ui.carrier.setEnabled(decoderEdit) elif constants.DECODING_DATAWHITENING in element: txt += "Texas Instruments CC110x chips allow a data whitening that is applied before sending the signals to HF. " \ "After a preamble (1010...) there is a fixed 16/32 bit sync word. The following data (incl. 16 bit CRC) " \ "is masked (XOR) with the output of a LFSR.\n" \ "This function removes the sync word, zeroes the crc (if valid) and unmasks the data." self.ui.optionWidget.setCurrentIndex(5) # Values can only be changed when editing decoder, otherwise default value if not decoderEdit: self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_applycrc.setChecked(True) self.ui.datawhitening_preamble_rm.setChecked(True) self.ui.datawhitening_sync_rm.setChecked(True) self.ui.datawhitening_crc_rm.setChecked(False) else: if element in self.chainoptions: value = self.chainoptions[element] if value == "": self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_applycrc.setChecked(True) self.ui.datawhitening_preamble_rm.setChecked(True) self.ui.datawhitening_sync_rm.setChecked(True) self.ui.datawhitening_crc_rm.setChecked(False) else: try: whitening_sync, whitening_polynomial, opt = value.split( ";") self.ui.datawhitening_sync.setText(whitening_sync) self.ui.datawhitening_polynomial.setText( whitening_polynomial) opt = self.e.hex2bit(opt) if len(opt) >= 4: self.ui.datawhitening_applycrc.setChecked( opt[0]) self.ui.datawhitening_preamble_rm.setChecked( opt[1]) self.ui.datawhitening_sync_rm.setChecked( opt[2]) self.ui.datawhitening_crc_rm.setChecked(opt[3]) except ValueError: self.ui.datawhitening_sync.setText("0xe9cae9ca") self.ui.datawhitening_polynomial.setText("0x21") self.ui.datawhitening_applycrc.setChecked(True) self.ui.datawhitening_preamble_rm.setChecked(True) self.ui.datawhitening_sync_rm.setChecked(True) self.ui.datawhitening_crc_rm.setChecked(False) self.ui.datawhitening_sync.setEnabled(decoderEdit) self.ui.datawhitening_polynomial.setEnabled(decoderEdit) self.ui.datawhitening_applycrc.setEnabled(decoderEdit) self.ui.datawhitening_preamble_rm.setEnabled(decoderEdit) self.ui.datawhitening_sync_rm.setEnabled(decoderEdit) self.ui.datawhitening_crc_rm.setEnabled(decoderEdit) self.ui.info.setText(txt) @pyqtSlot() def handle_datawhitening(self): opt = 0 if self.ui.datawhitening_applycrc.isChecked(): opt = opt | 0x8 if self.ui.datawhitening_preamble_rm.isChecked(): opt = opt | 0x4 if self.ui.datawhitening_sync_rm.isChecked(): opt = opt | 0x2 if self.ui.datawhitening_crc_rm.isChecked(): opt = opt | 0x1 datawhiteningstr = self.ui.datawhitening_sync.text( ) + ";" + self.ui.datawhitening_polynomial.text() + ";" + hex(opt) self.chainoptions[self.active_block] = datawhiteningstr self.decoderchainUpdate() @pyqtSlot() def handle_external(self): externalstr = self.ui.external_decoder.text( ) + ";" + self.ui.external_encoder.text() self.chainoptions[self.active_block] = externalstr self.decoderchainUpdate() @pyqtSlot() def handle_substitution_changed(self): subststr = "" for i in range(0, self.ui.substitution_rows.value()): if self.ui.substitution.item(i, 0) and self.ui.substitution.item( i, 1): subststr += self.ui.substitution.item( i, 0).text() + ":" + self.ui.substitution.item( i, 1).text() + ";" self.chainoptions[self.active_block] = subststr self.decoderchainUpdate() @pyqtSlot() def handle_substitution_rows_changed(self): # Substitution Row Spinbox self.ui.substitution.setRowCount(self.ui.substitution_rows.value()) self.decoderchainUpdate() @pyqtSlot() def handle_multiple_changed(self): # Multiple Spinbox val = self.ui.multiple.value() self.chainoptions[self.active_block] = val self.decoderchainUpdate() @pyqtSlot() def handle_carrier_changed(self): # Only allow {0, 1} carrier_txt = self.ui.carrier.text() if carrier_txt.count("0") + carrier_txt.count("1") < len(carrier_txt): self.ui.carrier.setText(self.old_carrier_txt) else: self.old_carrier_txt = carrier_txt # Carrier Textbox #self.e.carrier = self.e.str2bit(self.ui.carrier.text()) self.chainoptions[self.active_block] = carrier_txt self.decoderchainUpdate() def dragEnterEvent(self, event: QDragEnterEvent): event.accept() def dropEvent(self, event: QDropEvent): #if not self.ui.decoderchain.geometry().contains(self.mapToGlobal(event.pos())): if self.ui.decoderchain.active_element >= 0: self.chainoptions.pop( self.ui.decoderchain.item( self.ui.decoderchain.active_element).text(), None) self.ui.decoderchain.takeItem(self.ui.decoderchain.active_element) self.decoderchainUpdate() def set_signal(self): indx = self.ui.combobox_signals.currentIndex() if indx != 0: self.ui.inpt.setReadOnly(True) else: self.ui.inpt.setReadOnly(False) self.ui.inpt.setText("10010110") self.decoder_update() return signal = self.signals[indx - 1] pa = ProtocolAnalyzer(signal) pa.get_protocol_from_signal() self.ui.inpt.setText("".join(pa.decoded_proto_bits_str)) tmp_scene = QGraphicsScene() tmp_scene.addText(self.tr("Loading Signal...")) QApplication.setOverrideCursor(Qt.WaitCursor) self.ui.graphicsView_signal.setScene(tmp_scene) QApplication.processEvents() # scene = ZoomableScene() if signal is not None: last_block = pa.blocks[-1] lookup = pa.bit_sample_pos plot_data = signal.qad[lookup[0][0]:lookup[pa.num_blocks - 1][len(last_block) - 1]] self.ui.graphicsView_signal.plot_data(plot_data) # num_samples = len(plot_data) # minimum = -numpy.max(plot_data) # maximum = -numpy.min(plot_data) # scene.setSceneRect(0, minimum, num_samples, maximum - minimum) # scene.setBackgroundBrush(constants.BGCOLOR) # scene.addLine(0, 0, num_samples, 0, constants.AXISCOLOR) # precise_path = path_creator.create_precise_path(plot_data) # for subpath in precise_path: # scene.addPath(subpath, constants.LINECOLOR) # self.ui.graphicsView_signal.setScene(scene) # self.ui.graphicsView_signal.update() # self.ui.graphicsView_signal.scale(1, 1) self.ui.graphicsView_signal.centerOn(0, 0) QApplication.restoreOverrideCursor()