1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2026-07-02 17:30:47 +00:00

The dll_pll_veml_read_tracking_dump.py file was failing as it didn't read last

two fields (TOW_ms and WN). Probably because this file was written at a time when
these fields were not present in the tracking dump.
This commit fixes that, and plot_tracking_quality_indicator.py is operational now.
This script now also lays out a _RECORD_FORMAT such that it is easily maintainable
if the structure changes again in future in the dll_pll_veml_tracking.cc
Thanks!

Signed-off-by: minhaj <minhaj.sixbyte@gmail.com>
This commit is contained in:
minhaj
2026-06-19 17:29:08 -05:00
parent 2693aa48f9
commit ec718c6e64
3 changed files with 61 additions and 196 deletions
@@ -6,6 +6,7 @@
Opens GNSS-SDR tracking binary log file .dat and returns the contents
Irene Pérez Riega, 2023. iperrie@inta.es
Minhaj Uddin Ahmad, 2026. mahmad12@crimson.ua.edu
Args:
filename: path to file .dat with the raw data
@@ -25,198 +26,61 @@
"""
import struct
import sys
# Binary layout of one tracking dump record, matching the writer in
# src/algorithms/tracking/gnuradio_blocks/dll_pll_veml_tracking.cc
#
# Fields are written back-to-back.
# '<' selects little-endian / standard sizes / no alignment padding.
_RECORD_FORMAT = '<' + (
'f' # VE -> Magnitude of the Very Early correlator.
'f' # E -> Magnitude of the Early correlator.
'f' # P -> Magnitude of the Prompt correlator.
'f' # L -> Magnitude of the Late correlator.
'f' # VL -> Magnitude of the Very Late correlator.
'f' # prompt_I -> Prompt correlator, In-phase component.
'f' # prompt_Q -> Prompt correlator, Quadrature component.
'Q' # PRN_start_sample -> Sample counter from tracking start.
'f' # acc_carrier_phase_rad -> Accumulated carrier phase, in rad.
'f' # carrier_doppler_hz -> Doppler shift, in Hz.
'f' # carrier_doppler_rate_hz_s -> Doppler rate, in Hz/s.
'f' # code_freq_hz -> Code frequency, in chips/s.
'f' # code_freq_rate_hz_s -> Code frequency rate, in chips/s^2.
'f' # carr_error -> Raw carrier error at the PLL output, in Hz.
'f' # carr_nco -> Carrier error at the output of the PLL filter, in Hz.
'f' # code_error -> Raw code error at the DLL output, in chips.
'f' # code_nco -> Code error at the output of the DLL filter, in chips.
'f' # CN0_SNV_dB_Hz -> C/N0 estimation, in dB-Hz.
'f' # carrier_lock_test -> Output of the carrier lock test.
'f' # var1 -> aux variable.
'd' # var2 -> aux variable.
'I' # PRN -> Satellite ID.
'Q' # TOW_ms -> Time of week, in ms.
'I' # WN -> Week number.
)
_FIELD_NAMES = [
'VE', 'E', 'P', 'L', 'VL', 'prompt_I', 'prompt_Q', 'PRN_start_sample',
'acc_carrier_phase_rad', 'carrier_doppler_hz', 'carrier_doppler_rate_hz_s',
'code_freq_hz', 'code_freq_rate_hz_s', 'carr_error', 'carr_nco',
'code_error', 'code_nco', 'CN0_SNV_dB_Hz', 'carrier_lock_test',
'var1', 'var2', 'PRN', 'TOW_ms', 'WN',
]
def dll_pll_veml_read_tracking_dump (filename):
v1 = []
v2 = []
v3 = []
v4 = []
v5 = []
v6 = []
v7 = []
v8 = []
v9 = []
v10= []
v11 = []
v12 = []
v13 = []
v14 = []
v15 = []
v16 = []
v17 = []
v18 = []
v19 = []
v20 = []
v21 = []
v22 = []
GNSS_tracking = {}
record_size = struct.calcsize(_RECORD_FORMAT)
columns = [[] for _ in _FIELD_NAMES]
bytes_shift = 0
if sys.maxsize > 2 ** 36: # 64 bits computer
float_size_bytes = 4
unsigned_long_int_size_bytes = 8
double_size_bytes = 8
unsigned_int_size_bytes = 4
else: # 32 bits
float_size_bytes = 4
unsigned_long_int_size_bytes = 4
double_size_bytes = 8
unsigned_int_size_bytes = 4
f = open(filename, 'rb')
if f is None:
return None
else:
with open(filename, 'rb') as f:
while True:
f.seek(bytes_shift, 0)
# VE -> Magnitude of the Very Early correlator.
v1.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# E -> Magnitude of the Early correlator.
v2.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# P -> Magnitude of the Prompt correlator.
v3.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# L -> Magnitude of the Late correlator.
v4.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# VL -> Magnitude of the Very Late correlator.
v5.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# promp_I -> Value of the Prompt correlator in the In-phase
# component.
v6.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# promp_Q -> Value of the Prompt correlator in the Quadrature
# component.
v7.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# PRN_start_sample -> Sample counter from tracking start.
if unsigned_long_int_size_bytes == 8:
v8.append(struct.unpack(
'Q', f.read(unsigned_long_int_size_bytes))[0])
bytes_shift += unsigned_long_int_size_bytes
else:
v8.append(struct.unpack('I',
f.read(unsigned_int_size_bytes))[0])
bytes_shift += unsigned_int_size_bytes
f.seek(bytes_shift, 0)
# acc_carrier_phase_rad - > Accumulated carrier phase, in rad.
v9.append(struct.unpack('f', f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# carrier doppler hz -> Doppler shift, in Hz.
v10.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# carrier doppler rate hz s -> Doppler rate, in Hz/s.
v11.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# code freq hz -> Code frequency, in chips/s.
v12.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# code_freq_rate_hz_s -> Code frequency rate, in chips/s².
v13.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# carr_error -> Raw carrier error (unfiltered) at the PLL
# output, in Hz.
v14.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# carr_nco -> Carrier error at the output of the PLL
# filter, in Hz.
v15.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# code error -> Raw code error (unfiltered) at the DLL
# output, in chips.
v16.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# code nco -> Code error at the output of the DLL
# filter, in chips.
v17.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# CN0_SNV_dB_Hz -> C/N0 estimation, in dB-Hz.
v18.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# carrier lock test -> Output of the carrier lock test.
v19.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# var 1 -> not used ?
v20.append(struct.unpack('f',
f.read(float_size_bytes))[0])
bytes_shift += float_size_bytes
f.seek(bytes_shift, 0)
# var 2 -> not used ?
v21.append(struct.unpack('d',
f.read(double_size_bytes))[0])
bytes_shift += double_size_bytes
f.seek(bytes_shift, 0)
# PRN -> Satellite ID.
v22.append(struct.unpack('I',
f.read(unsigned_int_size_bytes))[0])
bytes_shift += unsigned_int_size_bytes
f.seek(bytes_shift, 0)
# Check file
linea = f.readline()
if not linea:
record = f.read(record_size)
if len(record) < record_size:
# Clean end of file (or a trailing partial record).
break
for column, value in zip(columns, struct.unpack(_RECORD_FORMAT,
record)):
column.append(value)
f.close()
GNSS_tracking['VE'] = v1
GNSS_tracking['E'] = v2
GNSS_tracking['P'] = v3
GNSS_tracking['L'] = v4
GNSS_tracking['VL'] = v5
GNSS_tracking['prompt_I'] = v6
GNSS_tracking['prompt_Q'] = v7
GNSS_tracking['PRN_start_sample'] = v8
GNSS_tracking['acc_carrier_phase_rad'] = v9
GNSS_tracking['carrier_doppler_hz'] = v10
GNSS_tracking['carrier_doppler_rate_hz_s'] = v11
GNSS_tracking['code_freq_hz'] = v12
GNSS_tracking['code_freq_rate_hz_s'] = v13
GNSS_tracking['carr_error'] = v14
GNSS_tracking['carr_nco'] = v15
GNSS_tracking['code_error'] = v16
GNSS_tracking['code_nco'] = v17
GNSS_tracking['CN0_SNV_dB_Hz'] = v18
GNSS_tracking['carrier_lock_test'] = v19
GNSS_tracking['var1'] = v20
GNSS_tracking['var2'] = v21
GNSS_tracking['PRN'] = v22
return GNSS_tracking
return dict(zip(_FIELD_NAMES, columns))
@@ -1,16 +1,15 @@
"""
plot_tracking_quality_indicators.py
Irene Pérez Riega, 2023. iperrie@inta.es
Minhaj Uddin Ahmad, 2026. mahmad12@crimson.ua.edu
Modifiable in the file:
channels - Number of channels
firs_channel - Number of the first channel
first_channel - Number of the first channel
path - Path to folder which contains raw files
fig_path - Path where doppler plots will be save
'trk_dump_ch' - Fixed part of the tracking dump files names
file_prefix - Fixed part of the tracking dump files names
-----------------------------------------------------------------------------
@@ -35,12 +34,13 @@ plot_names = []
# ---------- CHANGE HERE:
channels = 5
first_channel = 0
path = '/home/labnav/Desktop/TEST_IRENE/tracking'
fig_path = '/home/labnav/Desktop/TEST_IRENE/PLOTS/TrackingQualityIndicator'
path = '../../../out/'
fig_path = '../../../PLOTS/TrackingQualityIndicator'
file_prefix = "track_ch"
for N in range(1, channels + 1):
tracking_log_path = os.path.join(path,
f'trk_dump_ch{N-1+first_channel}.dat')
f'{file_prefix}{N-1+first_channel}.dat')
GNSS_tracking.append(dll_pll_veml_read_tracking_dump(tracking_log_path))
if not os.path.exists(fig_path):
@@ -59,7 +59,7 @@ plt.legend(plot_names)
plt.savefig(os.path.join(fig_path,
f'carrier_lock_test '
f'{str(round(np.mean(GNSS_tracking[n]["PRN"])))}'))
plt.show()
# plt.show()
# Second plot
plt.figure()
@@ -73,4 +73,4 @@ for n in range(len(GNSS_tracking)):
plt.legend(plot_names)
plt.savefig(os.path.join(
fig_path, f'SV {str(round(np.mean(GNSS_tracking[n]["PRN"])))}'))
plt.show()
# plt.show()