Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alignment action in analysis #458

Merged
merged 19 commits into from
Jun 24, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions data/ui/analysis.ui
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="16">
<item row="0" column="17">
<widget class="QToolButton" name="btnSaveProto">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
Expand Down Expand Up @@ -543,7 +543,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="11">
<item row="0" column="12">
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The &lt;span style=&quot; font-weight:600;&quot;&gt;Received Signal Strength Indicator&lt;/span&gt; indicates the average signal power of the current message.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
Expand All @@ -553,7 +553,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="6">
<item row="0" column="7">
<widget class="QLabel" name="lSlash">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
Expand All @@ -569,14 +569,14 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="3">
<item row="0" column="4">
<widget class="QLabel" name="lblShownRows">
<property name="text">
<string>shown: 42/108</string>
</property>
</widget>
</item>
<item row="0" column="15">
<item row="0" column="16">
<widget class="QLabel" name="lTime">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
Expand All @@ -598,7 +598,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="5">
<item row="0" column="6">
<widget class="QLabel" name="lSearchCurrent">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
Expand All @@ -617,7 +617,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="12">
<item row="0" column="13">
<widget class="QLabel" name="lblRSSI">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
Expand Down Expand Up @@ -645,14 +645,14 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
<bool>false</bool>
</property>
<property name="placeholderText">
<string>Search Pattern</string>
<string>Enter pattern here</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="8">
<item row="0" column="9">
<widget class="QToolButton" name="btnNextSearch">
<property name="enabled">
<bool>false</bool>
Expand All @@ -672,7 +672,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="4">
<item row="0" column="5">
<widget class="QToolButton" name="btnPrevSearch">
<property name="enabled">
<bool>false</bool>
Expand All @@ -692,7 +692,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="7">
<item row="0" column="8">
<widget class="QLabel" name="lSearchTotal">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
Expand All @@ -711,7 +711,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="14">
<item row="0" column="15">
<widget class="QLabel" name="label_3">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The &lt;span style=&quot; font-weight:600;&quot;&gt;Message Start&lt;/span&gt; is the point in time when a protocol message begins. Additionally the relative time (+ ...) from the previous message is shown.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
Expand All @@ -724,7 +724,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="13">
<item row="0" column="14">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
Expand Down Expand Up @@ -760,7 +760,7 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="9">
<item row="0" column="10">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
Expand All @@ -776,14 +776,14 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</spacer>
</item>
<item row="0" column="10">
<item row="0" column="11">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="17">
<item row="0" column="18">
<widget class="QToolButton" name="btnLoadProto">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
Expand All @@ -803,6 +803,13 @@ image: url(:/icons/icons/splitter_handle_vertical.svg);
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="lblClearAlignment">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;reset_alignment&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Reset alignment&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
Expand Down
36 changes: 31 additions & 5 deletions src/urh/controller/CompareFrameController.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ def __init__(self, plugin_manager: PluginManager, project_manager: ProjectManage
self.__active_group_ids = [0]
self.selected_protocols = set()

self.search_select_search_menu = QMenu()
self.search_action = self.search_select_search_menu.addAction(self.tr("Search"))
self.select_action = self.search_select_search_menu.addAction(self.tr("Select all"))
self.filter_action = self.search_select_search_menu.addAction(self.tr("Filter"))
self.ui.btnSearchSelectFilter.setMenu(self.search_select_search_menu)
self.search_select_filter_align_menu = QMenu()
self.search_action = self.search_select_filter_align_menu.addAction(self.tr("Search"))
self.select_action = self.search_select_filter_align_menu.addAction(self.tr("Select all"))
self.filter_action = self.search_select_filter_align_menu.addAction(self.tr("Filter"))
self.align_action = self.search_select_filter_align_menu.addAction(self.tr("Align"))
self.ui.btnSearchSelectFilter.setMenu(self.search_select_filter_align_menu)

self.analyze_menu = QMenu()
self.assign_participants_action = self.analyze_menu.addAction(self.tr("Assign participants"))
Expand All @@ -85,6 +86,7 @@ def __init__(self, plugin_manager: PluginManager, project_manager: ProjectManage
self.ui.btnAnalyze.setMenu(self.analyze_menu)

self.ui.lblShownRows.hide()
self.ui.lblClearAlignment.hide()

self.protocol_model = ProtocolTableModel(self.proto_analyzer, project_manager.participants,
self) # type: ProtocolTableModel
Expand Down Expand Up @@ -265,7 +267,9 @@ def create_connects(self):
self.search_action.triggered.connect(self.on_search_action_triggered)
self.select_action.triggered.connect(self.on_select_action_triggered)
self.filter_action.triggered.connect(self.on_filter_action_triggered)
self.align_action.triggered.connect(self.on_align_action_triggered)
self.ui.lblShownRows.linkActivated.connect(self.on_label_shown_link_activated)
self.ui.lblClearAlignment.linkActivated.connect(self.on_label_clear_alignment_link_activated)

self.protocol_label_list_model.protolabel_visibility_changed.connect(self.on_protolabel_visibility_changed)
self.protocol_label_list_model.protocol_label_name_edited.connect(self.label_value_model.update)
Expand Down Expand Up @@ -715,6 +719,12 @@ def __set_shown_rows_status_label(self):
else:
self.ui.lblShownRows.hide()

def align_messages(self, pattern=None):
pattern = self.ui.lineEditSearch.text() if pattern is None else pattern
self.proto_analyzer.align_messages(pattern, view_type=self.ui.cbProtoView.currentIndex())
self.ui.lblClearAlignment.setVisible(any(msg.alignment_offset != 0 for msg in self.proto_analyzer.messages))
self.protocol_model.update()

def next_search_result(self):
index = int(self.ui.lSearchCurrent.text())
self.ui.lSearchTotal.setText((str(len(self.protocol_model.search_results))))
Expand Down Expand Up @@ -1146,6 +1156,17 @@ def on_filter_action_triggered(self):
self.ui.btnSearchSelectFilter.clicked.disconnect()
self.ui.btnSearchSelectFilter.clicked.connect(self.filter_search_results)

@pyqtSlot()
def on_align_action_triggered(self):
def on_btn_search_select_filter_clicked():
self.align_messages()

self.ui.btnSearchSelectFilter.setText("Align")
self.ui.btnSearchSelectFilter.setIcon(QIcon.fromTheme("align-horizontal-left"))
self.set_search_ui_visibility(False)
self.ui.btnSearchSelectFilter.clicked.disconnect()
self.ui.btnSearchSelectFilter.clicked.connect(on_btn_search_select_filter_clicked)

@pyqtSlot(bool)
def on_writeable_changed(self, writeable_status: bool):
hidden_rows = {i for i in range(self.protocol_model.row_count) if self.ui.tblViewProtocol.isRowHidden(i)}
Expand Down Expand Up @@ -1434,6 +1455,11 @@ def on_label_shown_link_activated(self, link: str):
self.ui.lineEditSearch.clear()
self.show_all_rows()

@pyqtSlot(str)
def on_label_clear_alignment_link_activated(self, link: str):
if link == "reset_alignment":
self.align_messages(pattern="")

@pyqtSlot()
def on_protocol_updated(self):
self.set_shown_protocols()
Expand Down
4 changes: 4 additions & 0 deletions src/urh/models/ProtocolTableModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ def delete_range(self, min_row: int, max_row: int, start: int, end: int):

def flags(self, index: QModelIndex):
if index.isValid():
alignment_offset = self._get_alignment_offset(index.row())
if index.column() < alignment_offset:
return Qt.ItemIsSelectable | Qt.ItemIsEnabled

if self.is_writeable:
return Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsSelectable
else:
Expand Down
64 changes: 58 additions & 6 deletions src/urh/models/TableModel.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
from collections import defaultdict

import numpy
Expand All @@ -13,6 +14,8 @@
import array

class TableModel(QAbstractTableModel):
ALIGNMENT_CHAR = " "

data_edited = pyqtSignal(int, int)
vertical_header_color_status_changed = pyqtSignal(bool)

Expand Down Expand Up @@ -70,9 +73,14 @@ def proto_view(self):
def proto_view(self, value):
self._proto_view = value
if self._refindex >= 0:
self._diffs = self.protocol.find_differences(self._refindex, self._proto_view)
self._diffs = self.find_differences(self._refindex)
self.update()

def _get_alignment_offset(self, index: int):
f = 1 if self.proto_view == 0 else 4 if self.proto_view == 1 else 8
alignment_offset = int(math.ceil(self.protocol.messages[index].alignment_offset / f))
return alignment_offset

def __pad_until_index(self, row: int, bit_pos: int):
"""
Pad message in given row with zeros until given column so user can enter values behind end of message
Expand Down Expand Up @@ -135,7 +143,7 @@ def update(self):
self.col_count = numpy.max([len(msg) for msg in visible_messages])

if self._refindex >= 0:
self._diffs = self.protocol.find_differences(self._refindex, self.proto_view)
self._diffs = self.find_differences(self._refindex)
else:
self._diffs.clear()

Expand Down Expand Up @@ -213,12 +221,16 @@ def data(self, index: QModelIndex, role=Qt.DisplayRole):
j = index.column()
if role == Qt.DisplayRole and self.display_data:
try:
alignment_offset = self._get_alignment_offset(i)
if j < alignment_offset:
return self.ALIGNMENT_CHAR

if self.proto_view == 0:
return self.display_data[i][j]
return self.display_data[i][j-alignment_offset]
elif self.proto_view == 1:
return "{0:x}".format(self.display_data[i][j])
return "{0:x}".format(self.display_data[i][j-alignment_offset])
elif self.proto_view == 2:
return chr(self.display_data[i][j])
return chr(self.display_data[i][j-alignment_offset])
except IndexError:
return None

Expand Down Expand Up @@ -270,6 +282,8 @@ def setData(self, index: QModelIndex, value, role=Qt.DisplayRole):

i = index.row()
j = index.column()
a = self._get_alignment_offset(i)
j -= a
hex_chars = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f")

if i >= len(self.protocol.messages):
Expand Down Expand Up @@ -311,7 +325,45 @@ def find_protocol_value(self, value):
data = message.view_to_string(self.proto_view, self.decode)
j = data.find(value)
while j != -1:
self.search_results.append((i, j))
self.search_results.append((i, j + self._get_alignment_offset(i)))
j = data.find(value, j + 1)

return len(self.search_results)

def find_differences(self, refindex: int):
"""
Search all differences between protocol messages regarding a reference message

:param refindex: index of reference message
:rtype: dict[int, set[int]]
"""
differences = defaultdict(set)

if refindex >= len(self.protocol.messages):
return differences

if self.proto_view == 0:
proto = self.protocol.decoded_proto_bits_str
elif self.proto_view == 1:
proto = self.protocol.decoded_hex_str
elif self.proto_view == 2:
proto = self.protocol.decoded_ascii_str
else:
return differences

ref_message = proto[refindex]
ref_offset = self._get_alignment_offset(refindex)

for i, message in enumerate(proto):
if i == refindex:
continue

msg_offset = self._get_alignment_offset(i)
short, long = sorted([len(ref_message) + ref_offset, len(message) + msg_offset])

differences[i] = {
j for j in range(max(msg_offset, ref_offset), long)
if j >= short or message[j-msg_offset] != ref_message[j-ref_offset]
}

return differences
5 changes: 4 additions & 1 deletion src/urh/signalprocessing/Message.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class Message(object):

__slots__ = ["__plain_bits", "__bit_alignments", "pause", "modulator_index", "rssi", "participant", "message_type",
"absolute_time", "relative_time", "__decoder", "align_labels", "decoding_state", "timestamp",
"fuzz_created", "__decoded_bits", "__encoded_bits", "decoding_errors", "bit_len", "bit_sample_pos"]
"fuzz_created", "__decoded_bits", "__encoded_bits", "decoding_errors", "bit_len", "bit_sample_pos",
"alignment_offset"]

def __init__(self, plain_bits, pause: int, message_type: MessageType, rssi=0, modulator_index=0, decoder=None,
fuzz_created=False, bit_sample_pos=None, bit_len=100, participant=None):
Expand Down Expand Up @@ -51,6 +52,8 @@ def __init__(self, plain_bits, pause: int, message_type: MessageType, rssi=0, mo
self.align_labels = True
self.fuzz_created = fuzz_created

self.alignment_offset = 0

self.__decoded_bits = None
self.__encoded_bits = None
self.__bit_alignments = []
Expand Down
Loading