2525from typing import Optional
2626from skyfield .api import EarthSatellite , load , Timescale
2727import numpy as np
28+ from sgp4 .api import Satrec , WGS72 # type: ignore[import-untyped]
29+
30+ def corrupt_satellite (sat : EarthSatellite , mean_motion_factor : float = 0.8 ) -> EarthSatellite :
31+ """
32+ mean motion factor of >1.05 or <0.73 makes the data invalid
33+ 1.0 is the same.
34+ 1.01 - 1.04 affects correctness
35+ 0.74-0.93 affects accuracy and correctness
36+ 0.94-0.99 affects correctness
37+ """
38+ # Copy fields from the original model
39+ m = sat .model
40+
41+ # Build a new Satrec with modified mean motion
42+ corrupted_model = Satrec ()
43+ corrupted_model .sgp4init (
44+ WGS72 ,
45+ 'i' ,
46+ int (m .satnum ),
47+ float (m .jdsatepoch - 2433281.5 ), # epoch in days since 1949-12-31
48+ float (m .bstar ),
49+ float (m .ndot ),
50+ float (m .nddot ),
51+ float (m .ecco ),
52+ float (m .argpo ),
53+ float (m .inclo ),
54+ float (m .mo ),
55+ float (m .no_kozai ) * mean_motion_factor , # corrupted mean motion
56+ float (m .nodeo )
57+ )
58+
59+ # Create an EarthSatellite instance without calling __init__
60+ corrupted_sat = EarthSatellite .__new__ (EarthSatellite )
61+ corrupted_model .intldesg = sat .model .intldesg
62+ corrupted_sat .model = corrupted_model
63+ corrupted_sat .name = sat .name
64+ corrupted_sat .epoch = sat .epoch
65+ corrupted_sat .target = sat .target
66+
67+ return corrupted_sat
2868
2969
3070def build_tx_data_str (satellite_data : EarthSatellite ) -> str :
@@ -85,15 +125,18 @@ def build_tx_data_str(satellite_data: EarthSatellite) -> str:
85125 "MEAN_MOTION_DDOT" : motion_ddot
86126 })
87127
88- def build_earth_satellite_list_from_str (ts : Timescale , data : str ) -> list [Optional [EarthSatellite ]]:
128+ def build_earth_satellite_list_from_str (ts : Timescale , data : str ,
129+ make_faulty : bool ) -> list [Optional [EarthSatellite ]]:
89130 """
90131 Construct an EarthSatellite object from a string of data to be
91132 used for data validation.
92133
93134 Arguments:
94135 - ts: The timescale, used to build the satellite's epoch time
95- - data: a string of data (ideally in a dict format) to be used to populate
136+ - data: A string of data (ideally in a dict format) to be used to populate
96137 the attributes of the EarthSatellite object.
138+ - make_faulty: A boolean flag about whether the data generated should be faulty, to
139+ represent a malicious node
97140
98141 Returns:
99142 - A list of EarthSatellite objects
@@ -112,13 +155,21 @@ def build_earth_satellite_list_from_str(ts: Timescale, data: str) -> list[Option
112155 if not isinstance (data , list ) or not all (isinstance (d , dict ) for d in data ):
113156 raise TypeError ("Expected a list of dicts after parsing satellite data" )
114157
115- return [EarthSatellite .from_omm (ts , fields ) for fields in data ]
158+ sat_list = [EarthSatellite .from_omm (ts , fields ) for fields in data ]
116159
117- def load_json_data (file_name : str ) -> list [Optional [EarthSatellite ]]:
160+ if make_faulty :
161+ for i , sat in enumerate (sat_list ):
162+ sat_list [i ] = corrupt_satellite (sat )
163+
164+ return sat_list
165+
166+ def load_json_data (file_name : str , faulty_data : bool ) -> list [Optional [EarthSatellite ]]:
118167 """
119168 Turns json data in a file into a Python dict.
120169 JSON data must be in the Celestrak format
121170 https://rhodesmill.org/skyfield/earth-satellites.html
171+
172+ If faulty_data is True, this function will generate faulty data for testing
122173 """
123174 try :
124175 with load .open (file_name ) as f :
@@ -128,5 +179,5 @@ def load_json_data(file_name: str) -> list[Optional[EarthSatellite]]:
128179 return []
129180
130181 ts = load .timescale ()
131- satellite_list = build_earth_satellite_list_from_str (ts , data )
182+ satellite_list = build_earth_satellite_list_from_str (ts , data , faulty_data )
132183 return satellite_list
0 commit comments