Skip to content

Commit

Permalink
Merge pull request #40 from Algafix/general-dev
Browse files Browse the repository at this point in the history
New JSON logging with Schema
  • Loading branch information
Algafix authored Oct 2, 2024
2 parents 5d7619f + f4ed82a commit 65c2900
Show file tree
Hide file tree
Showing 35 changed files with 4,904 additions and 212 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/icd_10_corner_cases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ jobs:
- name: ICD Test Corner Cases with pytest
run: |
pytest tests/test_corner_cases.py
- name: Verify JSON output
run: |
npm install -g ajv-cli
ajv -s osnma/utils/json_schema/status_log_schema.json -d 'tests/logs/corner_cases_logs/logs*/*.json'
4 changes: 4 additions & 0 deletions .github/workflows/icd_10_test_vectors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ jobs:
- name: ICD Test Vectors with pytest
run: |
pytest tests/icd_test_vectors.py
- name: Verify JSON output
run: |
npm install -g ajv-cli
ajv -s osnma/utils/json_schema/status_log_schema.json -d 'tests/logs/icd_test_logs/logs*/*.json'
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ tests/test_corner_cases/*/OSNMA_last_KROOT.txt
# pycharm runs
.run/

**/status_log.json

# Test vectors extracted keys
/tests/icd_test_vectors/nmt_step1/OSNMA_PublicKey_1.xml
/tests/icd_test_vectors/nmt_step2/OSNMA_PublicKey_1.xml
Expand Down
2,882 changes: 2,882 additions & 0 deletions metrics/24_hours_statistics/24_hours_log.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions metrics/24_hours_statistics/OSNMA_MerkleTree.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?><signalData><header><GAL-header><source><GAL-EXT-GOC-SC-GLAd><mission>GAL</mission><segment>EXT</segment><element>GOC-SC</element></GAL-EXT-GOC-SC-GLAd></source><destination><GAL-EXT-GOC-SC-GLAd><mission>GAL</mission><segment>EXT</segment><element>GOC-SC</element></GAL-EXT-GOC-SC-GLAd></destination><issueDate>2024-01-15T10:00:00Z</issueDate><signalVersion>1.0</signalVersion><dataVersion>1.0</dataVersion></GAL-header></header><body><MerkleTree><UID>MT-22D568909FE0E92D2CE88F0B354E61BF827BB6A8007491A5A14C992C819A0F35</UID><Applicability><Begin>2024-01-15T10:00:00Z</Begin></Applicability><State>Applicable</State><SignatureFile>OSNMA_MerkleTree_20240115100000_newPKID_1.xml.p256</SignatureFile><SignatureVerificationCertificate>OSNMA_MerkleTree_20240115100000_newPKID_1.crt</SignatureVerificationCertificate><N>16</N><HashFunction>SHA-256</HashFunction><PublicKey><i>0</i><PKID>1</PKID><lengthInBits>264</lengthInBits><point>0397EB43789AA0F6D052A638468ECF5278E6F6DF8465ECB8D8B84B8C7A3501F73B</point><PKType>ECDSA P-256/SHA-256</PKType></PublicKey><TreeNode><j>4</j><i>0</i><lengthInBits>256</lengthInBits><x_ji>832E15EDE55655EAC6E399A539477B7C034CCE24C3C93FFC904ACD9BF842F04E</x_ji></TreeNode><TreeNode><j>3</j><i>1</i><lengthInBits>256</lengthInBits><x_ji>84DE3669E6DA551292979E5B8D045787FA967C57CC23638A30237614EDD9171A</x_ji></TreeNode><TreeNode><j>2</j><i>1</i><lengthInBits>256</lengthInBits><x_ji>DE73D209E4C5BCDC34CD117F2FE40FD08B110009997AD2B3291D3A2CF29943F9</x_ji></TreeNode><TreeNode><j>1</j><i>1</i><lengthInBits>256</lengthInBits><x_ji>6AAFDE28017BF0744D42819CE40E3A0CDA1ECA3F7A4EA67E134E7AA714C1E843</x_ji></TreeNode><TreeNode><j>0</j><i>1</i><lengthInBits>256</lengthInBits><x_ji>941BD34EA7DF668B6FC5BE75C1D93464D109BC615CB52C8124847FAFB09CBB2B</x_ji></TreeNode></MerkleTree></body></signalData>
1 change: 1 addition & 0 deletions metrics/24_hours_statistics/OSNMA_PublicKey_1.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?><signalData><header><GAL-header><source><GAL-EXT-GOC-SC-GLAd><mission>GAL</mission><segment>EXT</segment><element>GOC-SC</element></GAL-EXT-GOC-SC-GLAd></source><destination><GAL-EXT-GOC-SC-GLAd><mission>GAL</mission><segment>EXT</segment><element>GOC-SC</element></GAL-EXT-GOC-SC-GLAd></destination><issueDate>2024-01-15T10:00:00Z</issueDate><signalVersion>1.0</signalVersion><dataVersion>1.0</dataVersion></GAL-header></header><body><PublicKey><UID>PK-3A519D5B0E51F0E361AB46030E62C955271A3548DA1978F49EAEBC77EC67064E</UID><Applicability><Begin>2024-01-15T10:00:00Z</Begin></Applicability><State>Applicable</State><i>0</i><PKID>1</PKID><lengthInBits>264</lengthInBits><point>0397EB43789AA0F6D052A638468ECF5278E6F6DF8465ECB8D8B84B8C7A3501F73B</point><Certificate>OSNMA_PublicKey_20240115100000_newPKID_1.crt</Certificate><CRL>OSNMA_PublicKeyCRL_20240115100000_newPKID_1.crl</CRL><PKType>ECDSA P-256/SHA-256</PKType></PublicKey></body></signalData>
2 changes: 2 additions & 0 deletions metrics/24_hours_statistics/OSNMA_last_KROOT.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
21d049220518a76aad6d0357f1ce47d9c39687c040e9e0e2655b849ccf3ea095fdcaa386e1d4ed795128c845c0011df29a1786ee4e1ca5cbd2bddd6d66f473fdd714d4025bcd5be135461e43c2a35fb0b0e31eada3e1c06758682f9c9d58d3b6137b142609e7076f
72
139 changes: 139 additions & 0 deletions metrics/24_hours_statistics/get_data_change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import json
import numpy as np
import matplotlib.pyplot as plt
import tikzplotlib

FILENAME = './24_hours_log.json'

iod_info = {svid:{'value':[], 'tows': []} for svid in np.arange(1,37)}

def tikzplotlib_fix_ncols(obj):
"""
workaround for matplotlib 3.6 renamed legend's _ncol to _ncols, which breaks tikzplotlib
"""
if hasattr(obj, "_ncols"):
obj._ncol = obj._ncols
for child in obj.get_children():
tikzplotlib_fix_ncols(child)

def extract_data_change(file_name: str):

with open(file_name, 'r') as file:
status_json = json.load(file)

total_satellite_subframes = 0
total_subframes = 0
subframes_without_optimization = 0

for subframe in status_json:
tow = subframe["metadata"]['GST_subframe'][1]
total_subframes += 1
satellites_in_subframe = 0
satellites_with_iod_change = 0
for svid, data_info in subframe["nav_data_received"].items():
iod_block = iod_info[int(svid)]
iod = data_info["IOD"]
if iod is not None:
satellites_in_subframe += 1
total_satellite_subframes += 1
if len(iod_block['value']) == 0 or iod != iod_block['value'][-1]:
iod_block['value'].append(iod)
iod_block['tows'].append([tow, tow+30])
satellites_with_iod_change += 1
else:
iod_block['tows'][-1][1] = tow+30

if satellites_in_subframe < 4 or (satellites_in_subframe - satellites_with_iod_change) < 4:
subframes_without_optimization += 1


print(f"Total satellite subframes: {total_satellite_subframes}")
total_iod_changes = sum([len(iod_dict['value']) for iod_dict in iod_info.values()])
print(f"Total changes of IOD: {total_iod_changes}")
print(f"Subframes without change of data: {100 * (1 - total_iod_changes/total_satellite_subframes) :.2f}")

print('===================')
print(f"Total subframes: {total_subframes}")
print(f"Subframes without optimization: {subframes_without_optimization}")
print(f"Probability of no optimization on a given subframe: {100 * (1 - subframes_without_optimization/total_subframes) :.2f}")


# Visual of IODs
plt.figure()
for svid, iod_data in iod_info.items():
plt.broken_barh([(start, end-start) for start, end in iod_data['tows']], (svid-0.3, 0.6),
facecolors=('tab:orange', 'tab:green', 'tab:red'))
plt.title("IOD blocks over time")
plt.xlabel('ToW')
plt.ylabel('SVID')
plt.yticks(np.arange(1,37), [str(svid) for svid in np.arange(1,37)])
plt.ylim((0, 37))
plt.tight_layout()

iod_durations = [end - start for iod_data in iod_info.values() for start, end in iod_data['tows']]

# # Histogram of times
# plt.figure()
# values, counts = np.unique(iod_durations, return_counts=True)
# pvalues = [str(value) for value in values]
# plt.bar(pvalues, counts)
# plt.title("Histogram of IOD duration")
# plt.xlabel('ToW')
# plt.tight_layout()


# # Histogram of times (cap at 900s)
# bins = np.arange(30, 930, 30)
# clipped_iod_durations = np.clip(iod_durations, bins[0], bins[-1])
# all_counts = np.zeros(len(bins), dtype=int)
# values, counts = np.unique(clipped_iod_durations, return_counts=True)
# for value, count in zip(values, counts):
# idx = np.where(bins == value)
# all_counts[idx] = count
# labels = bins.astype(str)
# labels[-1] += '+'
# fig = plt.figure()
# plt.bar(labels, all_counts)
# plt.xticks(rotation=45)
# plt.title("Histogram of IOD duration over 24h")
# plt.xlabel('Seconds')
# plt.tight_layout()
#
# tikzplotlib_fix_ncols(fig)
# tikzplotlib.save(f"histogram_900s.tex")

# Histogram of times (cap at 750s)
bins = np.arange(30, 780, 30)
clipped_iod_durations = np.clip(iod_durations, bins[0], bins[-1])
all_counts = np.zeros(len(bins), dtype=int)
values, counts = np.unique(clipped_iod_durations, return_counts=True)
for value, count in zip(values, counts):
idx = np.where(bins == value)
all_counts[idx] = count
labels = bins.astype(str)
labels[-1] += '+'
fig = plt.figure()
plt.bar(labels, all_counts)
plt.xticks(rotation=45)
plt.title("Histogram of IOD duration over 24h")
plt.xlabel('Seconds')
plt.tight_layout()

tikzplotlib_fix_ncols(fig)
tikzplotlib.save(f"histogram_750s.tex")

# # CDF
# fig, ax1 = plt.subplots(1, 1, figsize=(10, 7))
# seconds_thr = np.arange(np.min(iod_durations),np.max(iod_durations)+1)
# cdf = np.array([np.sum(iod_durations <= thr) for thr in seconds_thr]) / len(iod_durations)*100
# plt.plot(seconds_thr, cdf)
# plt.ylabel('Percentage')
# plt.xlabel('TTFAF (s)')
# plt.title("Cumulative Distribution Plot (CDF) of the IOD duration over 24h")
# plt.grid()
# plt.tight_layout()

plt.show()

if __name__ == '__main__':
extract_data_change(FILENAME)
Binary file not shown.
35 changes: 35 additions & 0 deletions metrics/24_hours_statistics/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

import argparse
import sys
sys.path.insert(0, '..')

from osnma.receiver.receiver import OSNMAReceiver
from osnma.input_formats.input_sbf import SBF


parser = argparse.ArgumentParser(description='Runs OSNMAlib against the provided SBF file that contains the block '
'GALRawINAV.')
parser.add_argument('sbf_file', metavar='file', type=str, default='inav_2024-08-24.sbf', nargs='?',
help='name of the SBF file.')
args = parser.parse_args()


def sbf_current_config():
config_dict = {
'scenario_path': args.sbf_file,
'exec_path': '.',
'pubk_name': 'OSNMA_PublicKey_1.xml',
'merkle_name': 'OSNMA_MerkleTree.xml',
'do_json_status': True,
}

input_module = SBF(config_dict['scenario_path'])
osnma_r = OSNMAReceiver(input_module, config_dict)

osnma_r.start()


if __name__ == "__main__":

print(f"Running file {args.sbf_file}")
sbf_current_config()
16 changes: 8 additions & 8 deletions osnma/osnma_core/nav_data_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@

######## type annotations ########
from typing import TYPE_CHECKING, List, Dict, Tuple, Optional, Union
if TYPE_CHECKING:
from osnma.receiver.satellite import Satellite

######## imports ########
from osnma.structures.adkd import adkd_masks
from osnma.structures.mack_structures import TagAndInfo
from osnma.cryptographic.gst_class import GST
from osnma.utils.config import Config
from osnma.utils.status_logger import StatusLogger
from osnma.utils.exceptions import StoppedAtFAF

from bitstring import BitArray
Expand Down Expand Up @@ -225,10 +224,11 @@ def _handle_word_type_5(self, word_5_data: BitArray, gst_page: GST):

def add_word(self, word_type: int, page: BitArray, gst_page: GST):

StatusLogger.log_nav_data(self.svid, self.adkd, word_type)
adkd_data = self._get_adkd_data_from_word(page, word_type)

if word_type != 5:
iod = adkd_data[:10]
StatusLogger.log_nav_data_iod(self.svid, iod)
if self._is_new_adkd0_data_block(iod, gst_page):
new_adkd0 = ADKD0DataBlock(gst_page)
new_adkd0.add_word(word_type, adkd_data, gst_page)
Expand Down Expand Up @@ -328,6 +328,8 @@ def __repr__(self):

def add_word(self, word_type: int, full_page: BitArray, gst_page: GST):

StatusLogger.log_nav_data(self.svid, self.adkd, word_type)

new_adkd_data = self._get_adkd_data_from_word(full_page, word_type)
saved_words = self.words_per_type[word_type]

Expand Down Expand Up @@ -427,16 +429,14 @@ def new_tag_verified(self, tag: TagAndInfo):
else:
self.authenticated_data_dict[tag.data_id] = AuthenticatedData(tag)

def load_page(self, page: BitArray, gst_page: GST, satellite: 'Satellite'):
def load_page(self, page: BitArray, gst_page: GST, svid: int):
word_type = page[2:8].uint
if word_type not in self.active_words:
return
if word_type in WORDS_PER_ADKD[ADKD0]:
satellite.add_word(ADKD0, word_type)
self.adkd0_data_managers[satellite.svid].add_word(word_type, page, gst_page)
self.adkd0_data_managers[svid].add_word(word_type, page, gst_page)
else:
satellite.add_word(ADKD4, word_type)
self.adkd4_data_managers[satellite.svid].add_word(word_type, page, gst_page)
self.adkd4_data_managers[svid].add_word(word_type, page, gst_page)

def _calculate_TTFAF(self, auth_data: AuthenticatedData, gst_subframe: GST):

Expand Down
Loading

0 comments on commit 65c2900

Please sign in to comment.