UserContributedScripts: satelliteTracking.py

File satelliteTracking.py, 5.6 KB (added by jayce, 3 years ago)

Script to generate a satellite tracking SDF when given a TLE.

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5Script to generate a STEPPED mode SDF to track a satellite.
6"""
7
8import os
9import re
10import sys
11import ephem
12import numpy
13import getopt
14
15from lsl.common import sdf
16from lsl.common.stations import lwa1
17
18
19def usage(exitCode=None):
20        print """satelliteTracking - Create a STEPPED mode observation to track a satellite given
21 a file that contains a  NORAD two-line element set.
22
23Usage: satelliteTracking.py [OPTIONS] TLE_File YYYY/MM/DD HH:MM:SS.SSS
24
25Options:
26-h, --help                  Display this help information
27-u, --update-interval       Pointing update interval in seconds (Default = 15.0)
28-l, --obs-length            Duration of the observation in seconds (Default = 600.0)
29-b, --beam                  Beam to use for the observation (Default = 2)
30-1, --frequency1            Frequency in MHz for Tuning #1 (Default = 37.9 MHz)
31-2, --frequency2            Frequency in MHz for Tuning #2 (Default = 74.0 MHz)
32-f, --filter                DRX filter code (Default = 7)
33-s, --spec-setup            Spectrometer setup to use, i.e., "32 6144{Stokes=IV}"
34                            (Default = do not use DR spectrometer)
35-o, --output                Filename to save the SDF to (Default = satelliteTracking.sdf)
36"""
37        if exitCode is not None:
38                sys.exit(exitCode)
39        else:
40                return True
41
42
43def parseOptions(args):
44        config = {}
45        # Command line flags - default values
46        config['update'] = 15.0
47        config['duration'] = 600.0
48        config['beam'] = 2
49        config['freq1'] = 37.9e6
50        config['freq2'] = 74.0e6
51        config['filter'] = 7
52        config['spcSetup'] = [0, 0]
53        config['spcMetatag'] = ""
54        config['output'] = 'satelliteTracking.sdf'
55        config['args'] = []
56       
57        # Create the metatag regular expression to deal with spectrometer mode settings
58        metaRE = re.compile(r'\{.*\}')
59       
60        # Read in and process the command line flags
61        try:
62                opts, args = getopt.getopt(args, "hu:l:b:1:2:f:s:o:", ["help", "update-interval=", "obs-length=", "beam=", "frequency1=", "frequency2=", "filter=", "spec-setup=", "output="])
63        except getopt.GetoptError, err:
64                # Print help information and exit:
65                print str(err) # will print something like "option -a not recognized"
66                usage(exitCode=2)
67       
68        # Work through opts
69        for opt, value in opts:
70                if opt in ('-h', '--help'):
71                        usage(exitCode=0)
72                elif opt in ('-u', '--update-interval'):
73                        config['update'] = float(value)
74                elif opt in ('-l', '--obs-length'):
75                        config['duration'] = float(value)
76                elif opt in ('-b', '--beam'):
77                        config['beam'] = int(value)
78                elif opt in ('-1', '--frequency1'):
79                        config['freq1'] = float(value)*1e6
80                elif opt in ('-2', '--frequency2'):
81                        config['freq2'] = float(value)*1e6
82                elif opt in ('-f', '--filter'):
83                        config['filter'] = int(value)
84                elif opt in ('-s', '--spec-setup'):
85                        # Remove the ' marks
86                        value = value.replace("'", "")
87                        # Excise the metatags
88                        mtch = metaRE.search(value)
89                        if mtch is not None:
90                                metatag = mtch.group(0)
91                                value = metaRE.sub('', value)
92                        else:
93                                metatag = None
94                       
95                        config['spcSetup'] = [int(i) for i in value.lstrip().rstrip().split(None, 1)]
96                        config['spcMetatag'] = metatag
97                elif opt in ('-o', '--output'):
98                        config['output'] = value
99                else:
100                        assert False
101       
102        # Add in arguments
103        config['args'] = args
104       
105        # Validate
106        if config['beam'] not in (1, 2, 3, 4):
107                raise ValueError("Invalid beam assignment")
108        if config['freq1'] < 10e6 or config['freq1'] > 88e6:
109                raise ValueError("Invalid frequency assignment for tuning 1")
110        if config['freq2'] < 10e6 or config['freq2'] > 88e6:
111                raise ValueError("Invalid frequency assignment for tuning 2")
112        if config['filter'] not in (1, 2, 3, 4, 5, 6, 7):
113                raise ValueError("Invalid filter code")
114               
115        # Return configuration
116        return config
117
118
119def main(args):
120        config = parseOptions(args)
121
122        # Load in the TLE (only the first satellite)
123        fh = open(config['args'][0], 'r')
124        tle = fh.readlines()[:3]
125        fh.close()
126        pnt = ephem.readtle(*tle)
127        target = pnt.name
128       
129        # Observation start time
130        config['args'][1] = config['args'][1].replace('-', '/')
131        tStart = "%s %s" % (config['args'][1], config['args'][2])
132
133        # Create the SDF
134        observer = sdf.Observer("satelliteTracking.py Observer", 99)
135        session = sdf.Session("satelliteTracking.py Session", 1)
136        project = sdf.Project(observer, "satelliteTracking.py Project", 1, [session,])
137        obs1 = sdf.Stepped(target, target, tStart, config['filter'], RADec=False)
138        project.sessions[0].observations.append(obs1)
139        project.sessions[0].drxBeam = config['beam']
140        project.sessions[0].spcSetup = config['spcSetup']
141        project.sessions[0].spcMetatag = config['spcMetatag']
142
143        # Setup the observer to compute the position of the body
144        obs = lwa1.getObserver()
145        obs.date = tStart
146
147        # Go!
148        obs.date = tStart
149        pnt.compute(obs)
150        lastPos = (pnt.az*1.0, pnt.alt*1.0)
151       
152        tooFastWarning = True
153        for i in numpy.arange(0.0, config['duration'], config['update']):
154                obs.date = tStart
155                obs.date += (i + config['update']/2.0) / (3600.0*24.0)
156               
157                pnt.compute(obs)
158                az = float(pnt.az) * 180.0/numpy.pi
159                el = float(pnt.alt) *180.0/numpy.pi
160                if ephem.separation(lastPos, (pnt.az, pnt.alt)) > ephem.degrees('1:30:00'):
161                        if tooFastWarning:
162                                print "WARNING: Target has moved more than 1.5 degrees since last update"
163                                print "         Consider reducing the update interval"
164                                tooFastWarning = False
165                lastPos = (pnt.az*1.0, pnt.alt*1.0)
166               
167                stp = sdf.BeamStep(az, el, str(config['update']), config['freq1'], config['freq2'], RADec=False)
168                obs1.append(stp)
169       
170        # Write it out
171        if os.path.exists(config['output']):
172                raise RuntimeError("File '%s' already exists" % config['output'])
173        project.render(verbose=True)
174        fh = open(config['output'], 'w')
175        fh.write(project.render())
176        fh.close()
177
178
179if __name__ == "__main__":
180        main(sys.argv[1:])