2323 'async' : "vires_fetch_filtered_data_async.xml" ,
2424 'model_info' : "vires_get_model_info.xml" ,
2525 'times_from_orbits' : "vires_times_from_orbits.xml" ,
26- 'get_observatories' : 'vires_get_observatories.xml'
26+ 'get_observatories' : 'vires_get_observatories.xml' ,
27+ 'get_conjunctions' : 'vires_get_conjunctions.xml' ,
2728}
2829
2930REFERENCES = {
@@ -401,8 +402,6 @@ class SwarmRequest(ClientRequest):
401402
402403 Args:
403404 url (str):
404- username (str):
405- password (str):
406405 token (str):
407406 config (str or ClientConfig):
408407 logging_level (str):
@@ -415,6 +414,10 @@ class SwarmRequest(ClientRequest):
415414 'CryoSat-2' : None ,
416415 }
417416
417+ CONJUNCTION_MISISON_SPACECRAFT_PAIRS = {
418+ (('Swarm' , 'A' ), ('Swarm' , 'B' )),
419+ }
420+
418421 COLLECTIONS = {
419422 "MAG" : ["SW_OPER_MAG{}_LR_1B" .format (x ) for x in "ABC" ],
420423 "MAG_HR" : ["SW_OPER_MAG{}_HR_1B" .format (x ) for x in "ABC" ],
@@ -728,7 +731,7 @@ class SwarmRequest(ClientRequest):
728731 "B_FGM1" , "B_FGM2" , "B_FGM3" , "q_NEC_CRF" , "q_error" ,
729732 ],
730733 "MAG_GRACE" : [
731- "F" , "B_NEC" , "B_NEC_raw" , "B_FGM" , "B_mod_NEC" ,
734+ "F" , "B_NEC" , "B_NEC_raw" , "B_FGM" ,
732735 "q_NEC_CRF" , "q_error" ,
733736 ],
734737 "MAG_GFO" : [
@@ -770,10 +773,10 @@ class SwarmRequest(ClientRequest):
770773 "MCO_SHA_2X" , "CHAOS" , "CHAOS-MMA" , "MMA_SHA_2C" , "MMA_SHA_2F" , "MIO_SHA_2C" , "MIO_SHA_2D" , "SwarmCI" ,
771774 ]
772775
773- def __init__ (self , url = None , username = None , password = None , token = None ,
776+ def __init__ (self , url = None , token = None ,
774777 config = None , logging_level = "NO_LOGGING" ):
775778 super ().__init__ (
776- url , username , password , token , config , logging_level ,
779+ url , token , config , logging_level ,
777780 server_type = "Swarm"
778781 )
779782
@@ -1309,16 +1312,38 @@ def get_times_for_orbits(self, start_orbit, end_orbit, mission="Swarm", spacecra
13091312 start_orbit = int (start_orbit )
13101313 end_orbit = int (end_orbit )
13111314
1312- if mission not in self .MISSION_SPACECRAFTS :
1313- raise ValueError (
1314- f"Invalid mission { mission } !"
1315- f"Allowed options are: { ',' .join (self .MISSION_SPACECRAFTS )} "
1316- )
1315+ # Change to spacecraft = "A" etc. for this request
1316+ spacecraft = self ._fix_spacecraft (mission , spacecraft )
1317+ self ._check_mission_spacecraft (mission , spacecraft )
1318+
1319+ templatefile = TEMPLATE_FILES ["times_from_orbits" ]
1320+ template = JINJA2_ENVIRONMENT .get_template (templatefile )
1321+ request = template .render (
1322+ mission = mission ,
1323+ spacecraft = spacecraft ,
1324+ start_orbit = start_orbit ,
1325+ end_orbit = end_orbit
1326+ ).encode ('UTF-8' )
1327+ response = self ._get (
1328+ request , asynchronous = False , show_progress = False )
1329+ responsedict = json .loads (response .decode ('UTF-8' ))
1330+ start_time = parse_datetime (responsedict ['start_time' ])
1331+ end_time = parse_datetime (responsedict ['end_time' ])
1332+ return start_time , end_time
13171333
1334+ def _fix_spacecraft (self , mission , spacecraft ):
13181335 # Change to spacecraft = "A" etc. for this request
13191336 spacecraft = str (spacecraft ) if spacecraft is not None else None
13201337 if mission == "Swarm" and spacecraft in ("Alpha" , "Bravo" , "Charlie" ):
13211338 spacecraft = spacecraft [0 ]
1339+ return spacecraft
1340+
1341+ def _check_mission_spacecraft (self , mission , spacecraft ):
1342+ if mission not in self .MISSION_SPACECRAFTS :
1343+ raise ValueError (
1344+ f"Invalid mission { mission } !"
1345+ f"Allowed options are: { ',' .join (self .MISSION_SPACECRAFTS )} "
1346+ )
13221347
13231348 if self .MISSION_SPACECRAFTS [mission ]:
13241349 # missions with required spacecraft id
@@ -1339,20 +1364,6 @@ def get_times_for_orbits(self, start_orbit, end_orbit, mission="Swarm", spacecra
13391364 "Set spacecraft to None."
13401365 )
13411366
1342- templatefile = TEMPLATE_FILES ["times_from_orbits" ]
1343- template = JINJA2_ENVIRONMENT .get_template (templatefile )
1344- request = template .render (
1345- mission = mission ,
1346- spacecraft = spacecraft ,
1347- start_orbit = start_orbit ,
1348- end_orbit = end_orbit
1349- ).encode ('UTF-8' )
1350- response = self ._get (
1351- request , asynchronous = False , show_progress = False )
1352- responsedict = json .loads (response .decode ('UTF-8' ))
1353- start_time = parse_datetime (responsedict ['start_time' ])
1354- end_time = parse_datetime (responsedict ['end_time' ])
1355- return start_time , end_time
13561367
13571368 def get_orbit_number (self , spacecraft , input_time , mission = "Swarm" ):
13581369 """Translate a time to an orbit number.
@@ -1506,3 +1517,89 @@ def _check_deprecated_models(models):
15061517 print ("WARNING: Model {} is deprecated. {}" .format (
15071518 deprecated_model , DEPRECATED_MODELS [deprecated_model ]
15081519 ), file = sys .stdout )
1520+
1521+
1522+ def get_conjunctions (self , start_time = None , end_time = None , threshold = 1.0 ,
1523+ spacecraft1 = 'A' , spacecraft2 = 'B' ,
1524+ mission1 = 'Swarm' , mission2 = 'Swarm' ):
1525+ """ Get times of the spacecraft conjunctions. Currently available for
1526+ the following spacecraft pairs:
1527+ - Swarm-A/Swarm-B
1528+
1529+ Args:
1530+ start_time (datetime / ISO_8601 string): optional start time
1531+ end_time (datetime / ISO_8601 string): optional end time
1532+ threshold (float): optional maximum allowed angular separation
1533+ in degrees; by default set to 1; allowed values are [0, 180]
1534+ spacecraft1: identifier of the first spacecraft, default to 'A'
1535+ spacecraft2: identifier of the second spacecraft, default to 'B'
1536+ mission1 (str): mission of the first spacecraft, defaults to 'Swarm'
1537+ mission2 (str): mission of the first spacecraft, defaults to 'Swarm'
1538+
1539+ Returns:
1540+ ReturnedData:
1541+ """
1542+ try :
1543+ start_time = parse_datetime (start_time ) if start_time else None
1544+ end_time = parse_datetime (end_time ) if end_time else None
1545+ except TypeError :
1546+ raise TypeError (
1547+ "start_time and end_time must be datetime objects or ISO-8601 "
1548+ "date/time strings"
1549+ ) from None
1550+
1551+ if not (0 <= threshold <= 180 ):
1552+ raise ValueError ("Invalid threshold value!" )
1553+
1554+ spacecraft1 = self ._fix_spacecraft (mission1 , spacecraft1 )
1555+ spacecraft2 = self ._fix_spacecraft (mission2 , spacecraft2 )
1556+
1557+ self ._check_mission_spacecraft (mission1 , spacecraft1 )
1558+ self ._check_mission_spacecraft (mission2 , spacecraft2 )
1559+
1560+ if (mission1 , spacecraft1 ) == (mission2 , spacecraft2 ):
1561+ raise ValueError (
1562+ "The first and second spacecraft must not be the same!"
1563+ )
1564+
1565+ spacecraft_pair = tuple (sorted ([
1566+ (mission1 , spacecraft1 ), (mission2 , spacecraft2 )
1567+ ]))
1568+
1569+ if spacecraft_pair not in self .CONJUNCTION_MISISON_SPACECRAFT_PAIRS :
1570+ raise ValueError (
1571+ "Conjunctions not available for the requested "
1572+ "spacecraft pair {spacecraft_pair}!"
1573+ )
1574+
1575+ templatefile = TEMPLATE_FILES ["get_conjunctions" ]
1576+ template = JINJA2_ENVIRONMENT .get_template (templatefile )
1577+ request = template .render (
1578+ begin_time = start_time ,
1579+ end_time = end_time ,
1580+ spacecraft1 = spacecraft1 ,
1581+ spacecraft2 = spacecraft2 ,
1582+ mission1 = mission1 ,
1583+ mission2 = mission2 ,
1584+ threshold = threshold ,
1585+ ).encode ('UTF-8' )
1586+
1587+ show_progress = False
1588+ leave_progress_bar = False
1589+ response = ReturnedDataFile (filetype = "cdf" )
1590+
1591+ response_handler = self ._response_handler (
1592+ retdatafile = response ,
1593+ show_progress = show_progress ,
1594+ leave_progress_bar = leave_progress_bar ,
1595+ )
1596+
1597+ self ._get (
1598+ request = request ,
1599+ asynchronous = False ,
1600+ show_progress = show_progress ,
1601+ leave_progress_bar = leave_progress_bar ,
1602+ response_handler = response_handler ,
1603+ )
1604+
1605+ return response
0 commit comments