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

Feature/signal real timestamp #969

Merged
merged 4 commits into from
Nov 16, 2023
Merged
Changes from 1 commit
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
Next Next commit
Adding timestamps to Recorded Signals and ProtocolAnalyzer messages
Jose Velazquez authored and PepsConti committed Nov 15, 2023
commit 968712e328ef2200703d315420a1796767829f00
20 changes: 14 additions & 6 deletions src/urh/controller/MainController.py
Original file line number Diff line number Diff line change
@@ -391,7 +391,9 @@ def add_simulator_profile(self, filename):
self.ui.tabWidget.setCurrentIndex(3)
self.simulator_tab_controller.load_simulator_file(filename)

def add_signalfile(self, filename: str, group_id=0, enforce_sample_rate=None):
def add_signalfile(
self, filename: str, group_id=0, enforce_sample_rate=None, signal_timestamp=0
):
if not os.path.exists(filename):
QMessageBox.critical(
self,
@@ -411,7 +413,9 @@ def add_signalfile(self, filename: str, group_id=0, enforce_sample_rate=None):
else:
sample_rate = self.project_manager.device_conf["sample_rate"]

signal = Signal(filename, sig_name, sample_rate=sample_rate)
signal = Signal(
filename, sig_name, sample_rate=sample_rate, timestamp=signal_timestamp
)

self.file_proxy_model.open_files.add(filename)
self.add_signal(signal, group_id)
@@ -944,11 +948,15 @@ def on_show_spectrum_dialog_action_triggered(self):
r.device_parameters_changed.connect(pm.set_device_parameters)
r.show()

@pyqtSlot(list, float)
def on_signals_recorded(self, file_names: list, sample_rate: float):
@pyqtSlot(list)
def on_signals_recorded(self, recorded_files: list):
QApplication.instance().setOverrideCursor(Qt.WaitCursor)
for filename in file_names:
self.add_signalfile(filename, enforce_sample_rate=sample_rate)
for recorded_file in recorded_files:
self.add_signalfile(
recorded_file.filename,
enforce_sample_rate=recorded_file.sample_rate,
signal_timestamp=recorded_file.timestamp,
)
QApplication.instance().restoreOverrideCursor()

@pyqtSlot()
24 changes: 13 additions & 11 deletions src/urh/controller/dialogs/ReceiveDialog.py
Original file line number Diff line number Diff line change
@@ -9,10 +9,11 @@
from urh.util import FileOperator
from urh.util.Formatter import Formatter
from datetime import datetime
from urh.signalprocessing.RecordedFile import RecordedFile


class ReceiveDialog(SendRecvDialog):
files_recorded = pyqtSignal(list, float)
files_recorded = pyqtSignal(list)

def __init__(self, project_manager, parent=None, testing_mode=False):
try:
@@ -53,12 +54,7 @@ def save_before_close(self):
elif reply == QMessageBox.Abort:
return False

try:
sample_rate = self.device.sample_rate
except:
sample_rate = 1e6

self.files_recorded.emit(self.recorded_files, sample_rate)
self.files_recorded.emit(self.recorded_files)
return True

def update_view(self):
@@ -109,9 +105,11 @@ def on_save_clicked(self):

dev = self.device
big_val = Formatter.big_value_with_suffix
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
timestamp_str = datetime.fromtimestamp(dev.data_timestamp).strftime(
"%Y%m%d_%H%M%S"
)
initial_name = "{0}-{1}-{2}Hz-{3}Sps".format(
dev.name, timestamp, big_val(dev.frequency), big_val(dev.sample_rate)
dev.name, timestamp_str, big_val(dev.frequency), big_val(dev.sample_rate)
)

if dev.bandwidth_is_adjustable:
@@ -125,5 +123,9 @@ def on_save_clicked(self):
initial_name, data, sample_rate=dev.sample_rate, parent=self
)
self.already_saved = True
if filename is not None and filename not in self.recorded_files:
self.recorded_files.append(filename)
if filename is not None and filename not in (
x.filename for x in self.recorded_files
):
self.recorded_files.append(
RecordedFile(filename, dev.sample_rate, dev.data_timestamp)
)
1 change: 1 addition & 0 deletions src/urh/controller/dialogs/SendRecvDialog.py
Original file line number Diff line number Diff line change
@@ -157,6 +157,7 @@ def _create_device_connects(self):
def reset(self):
self.device.current_index = 0
self.device.current_iteration = 0
self.device.reset_data_timestamp()
self.ui.lSamplesCaptured.setText("0")
self.ui.lSignalSize.setText("0")
self.ui.lTime.setText("0")
24 changes: 23 additions & 1 deletion src/urh/dev/VirtualDevice.py
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@ def __init__(
self.name = name
self.mode = mode
self.backend_handler = backend_handler
self.__data_timestamp = 0

freq = config.DEFAULT_FREQUENCY if freq is None else freq
sample_rate = config.DEFAULT_SAMPLE_RATE if sample_rate is None else sample_rate
@@ -493,7 +494,10 @@ def baseband_gain(self, value):

@property
def sample_rate(self):
return self.__dev.sample_rate
try:
return self.__dev.sample_rate
except:
return 1e6

@sample_rate.setter
def sample_rate(self, value):
@@ -631,6 +635,23 @@ def data(self, value):
"{}:{} has no data".format(self.__class__.__name__, self.backend.name)
)

@property
def data_timestamp(self):
if self.backend == Backends.native:
try:
self.__data_timestamp = (
self.__dev.first_data_timestamp
) # more accurate timestamp
except:
pass
return self.__data_timestamp

def reset_data_timestamp(self):
if self.backend == Backends.native:
self.__dev.reset_first_data_timestamp()
else:
self.__data_timestamp = time.time()

def free_data(self):
if self.backend == Backends.grc:
self.__dev.data = None
@@ -740,6 +761,7 @@ def spectrum(self):
raise ValueError("Spectrum x only available in spectrum mode")

def start(self):
self.__data_timestamp = time.time()
if self.backend == Backends.grc:
self.__dev.setTerminationEnabled(True)
self.__dev.terminate()
21 changes: 21 additions & 0 deletions src/urh/dev/native/Device.py
Original file line number Diff line number Diff line change
@@ -304,6 +304,7 @@ def __init__(
self.error_codes = {}
self.device_messages = []

self.__first_data_timestamp = 0
self.receive_process_function = self.device_receive
self.send_process_function = self.device_send

@@ -654,7 +655,15 @@ def set_device_direct_sampling_mode(self, value):
except (BrokenPipeError, OSError):
pass

@property
def first_data_timestamp(self):
return self.__first_data_timestamp

def reset_first_data_timestamp(self):
self.__first_data_timestamp = 0

def start_rx_mode(self):
self.__first_data_timestamp = 0
self.init_recv_buffer()
self.parent_data_conn, self.child_data_conn = Pipe(duplex=False)
self.parent_ctrl_conn, self.child_ctrl_conn = Pipe()
@@ -783,8 +792,20 @@ def read_receiving_queue(self):
while self.is_receiving:
try:
byte_buffer = self.parent_data_conn.recv_bytes()

if self.__first_data_timestamp == 0:
self.__first_data_timestamp = time.time()
calculating_timestamp = True
else:
calculating_timestamp = False

samples = self.bytes_to_iq(byte_buffer)
n_samples = len(samples)

if calculating_timestamp:
# Timestamp accurate correction
self.__first_data_timestamp -= n_samples / self.sample_rate

if n_samples == 0:
continue

8 changes: 7 additions & 1 deletion src/urh/signalprocessing/Message.py
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ def __init__(
samples_per_symbol=100,
participant=None,
bits_per_symbol=1,
timestamp=0,
):
"""

@@ -75,7 +76,12 @@ def __init__(
self.participant = participant # type: Participant
self.message_type = message_type # type: MessageType

self.timestamp = time.time()
if timestamp == 0:
self.timestamp = time.time()
else:
# Caller passed specific timestamp for this message
self.timestamp = timestamp

self.absolute_time = 0 # set in Compare Frame
self.relative_time = 0 # set in Compare Frame

4 changes: 4 additions & 0 deletions src/urh/signalprocessing/ProtocolAnalyzer.py
Original file line number Diff line number Diff line change
@@ -267,6 +267,9 @@ def get_protocol_from_signal(self):
middle_bit_pos = bit_sample_pos[i][int(len(bits) / 2)]
start, end = middle_bit_pos, middle_bit_pos + samples_per_symbol
rssi = np.mean(signal.iq_array.subarray(start, end).magnitudes_normalized)
message_timestamp = signal.timestamp + (
bit_sample_pos[i][0] / signal.sample_rate
)
message = Message(
bits,
pause,
@@ -276,6 +279,7 @@ def get_protocol_from_signal(self):
decoder=self.decoder,
bit_sample_pos=bit_sample_pos[i],
bits_per_symbol=signal.bits_per_symbol,
timestamp=message_timestamp,
)
self.messages.append(message)
i += 1
5 changes: 5 additions & 0 deletions src/urh/signalprocessing/RecordedFile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RecordedFile:
def __init__(self, filename, sample_rate, timestamp):
self.filename = filename
self.sample_rate = sample_rate
self.timestamp = timestamp
6 changes: 6 additions & 0 deletions src/urh/signalprocessing/Signal.py
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ def __init__(
name="Signal",
modulation: str = None,
sample_rate: float = 1e6,
timestamp: float = 0,
parent=None,
):
super().__init__(parent)
@@ -58,6 +59,7 @@ def __init__(
self.__center = 0
self._noise_threshold = 0
self.__sample_rate = sample_rate
self.__timestamp = timestamp
self.noise_min_plot = 0
self.noise_max_plot = 0
self.block_protocol_update = False
@@ -224,6 +226,10 @@ def sample_rate(self, val):
self.__sample_rate = val
self.sample_rate_changed.emit(val)

@property
def timestamp(self):
return self.__timestamp

@property
def parameter_cache(self) -> dict:
"""