Skip to content

Commit

Permalink
Merge pull request #86 from mraspaud/fix-noaa-20
Browse files Browse the repository at this point in the history
Add a test for runing from start to finish
  • Loading branch information
adybbroe authored Apr 28, 2024
2 parents d2e984b + 8cb255a commit 0a9c19a
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 26 deletions.
35 changes: 21 additions & 14 deletions trollsched/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,48 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Graph manipulation.
"""
"""Graph manipulation."""
import numpy as np


class Graph(object):
class Graph():
"""A graph class."""

def __init__(self, n_vertices=None, adj_matrix=None):
"""Set up the graph."""
if n_vertices is not None:
self.order = n_vertices
self.vertices = np.arange(self.order)
self.adj_matrix = np.zeros((self.order, self.order), np.bool)
self.weight_matrix = np.zeros((self.order, self.order), np.float)
self.adj_matrix = np.zeros((self.order, self.order), bool)
self.weight_matrix = np.zeros((self.order, self.order), float)
elif adj_matrix is not None:
self.order = adj_matrix.shape[0]
self.vertices = np.arange(self.order)
self.adj_matrix = adj_matrix
self.weight_matrix = np.zeros_like(adj_matrix)

def weight(self, u, v):
"""weight of the *u*-*v* edge.
"""
"""Weight of the *u*-*v* edge."""
return self.weight_matrix[u, v]

def neighbours(self, v):
"""Find neighbours."""
return self.vertices[self.adj_matrix[v, :] != 0]

def add_edge(self, v1, v2, weight=1):
"""Add an edge."""
self.weight_matrix[v1, v2] = weight
self.weight_matrix[v2, v1] = weight
self.adj_matrix[v1, v2] = True
self.adj_matrix[v2, v1] = True

def add_arc(self, v1, v2, weight=1):
"""Add an arc."""
self.adj_matrix[v1, v2] = True
self.weight_matrix[v1, v2] = weight

def bron_kerbosch(self, r, p, x):
"""Get the maximal cliques.
"""
"""Get the maximal cliques."""
if len(p) == 0 and len(x) == 0:
yield r
for v in p:
Expand All @@ -71,7 +73,9 @@ def bron_kerbosch(self, r, p, x):
x = x | set((v, ))

def dag_longest_path(self, v1, v2=None):
"""Give the longest path from *v1* to all other vertices or *v2* if
"""Find the longest path between v1 and v2.
Give the longest path from *v1* to all other vertices or *v2* if
specified. Assumes the vertices are sorted topologically and that the
graph is directed and acyclic (DAG).
"""
Expand All @@ -81,7 +85,9 @@ def dag_longest_path(self, v1, v2=None):
return dist, path

def dag_shortest_path(self, v1, v2=None):
"""Give the sortest path from *v1* to all other vertices or *v2* if
"""Find the shortest path between v1 and v2.
Give the shortest path from *v1* to all other vertices or *v2* if
specified. Assumes the vertices are sorted topologically and that the
graph is directed and acyclic (DAG). *v1* and *v2* are the indices of
the vertices in the vertice list.
Expand Down Expand Up @@ -111,22 +117,23 @@ def dag_shortest_path(self, v1, v2=None):
return dists[v2], path

def save(self, filename):
"""Save a file."""
np.savez_compressed(filename,
adj=self.adj_matrix,
weights=self.weight_matrix)

def load(self, filename):
"""Load a file."""
stuff = np.load(filename)
self.adj_matrix = stuff["adj"]
self.weight_matrix = stuff["weights"]
self.order = self.adj_matrix.shape[0]
self.vertices = np.arange(self.order)

def export(self, filename="./sched.gv", labels=None):
"""dot sched.gv -Tpdf -otruc.pdf
"""
"""dot sched.gv -Tpdf -otruc.pdf."""
with open(filename, "w") as fd_:
fd_.write("digraph schedule { \n size=\"80, 10\";\n center=\"1\";\n")
fd_.write('digraph schedule { \n size="80, 10";\n center="1";\n')
for v1 in range(1, self.order - 1):
for v2 in range(1, self.order - 1):
if self.adj_matrix[v1, v2]:
Expand Down
19 changes: 9 additions & 10 deletions trollsched/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@
# Older versions of pyresample:
from pyresample.utils import parse_area_file

from pyresample.boundary import AreaDefBoundary

from trollsched import utils
from trollsched import MIN_PASS, utils
from trollsched.combine import get_combined_sched
from trollsched.graph import Graph
from trollsched.satpass import SimplePass, get_next_passes
Expand All @@ -53,7 +52,7 @@ class Station:
"""docstring for Station."""

def __init__(self, station_id, name, longitude, latitude, altitude, area, satellites, area_file=None,
min_pass=None, local_horizon=0):
min_pass=MIN_PASS, local_horizon=0):
"""Initialize the station."""
self.id = station_id
self.name = name
Expand Down Expand Up @@ -96,7 +95,7 @@ def single_station(self, sched, start_time, tle_file):

allpasses = self.get_next_passes(opts, sched, start_time, tle_file)

area_boundary = AreaDefBoundary(self.area, frequency=500)
area_boundary = self.area.boundary(8)
self.area.poly = area_boundary.contour_poly

if opts.plot:
Expand Down Expand Up @@ -538,15 +537,15 @@ def send_file(url, file):
del pathname
if url.scheme in ["file", ""]:
pass
elif url.scheme == "ftp":
elif url.scheme in ["ftp", b"ftp"]:
import ftplib
session = ftplib.FTP(url.hostname, url.username, url.password)
with open(file, "rb") as xfile:
session.storbinary("STOR " + str(filename), xfile)
session.quit()
else:
logger.error("Cannot save to %s, but file is there:",
str(url.scheme), str(file))
(str(url.scheme), str(file)))


def combined_stations(scheduler, start_time, graph, allpasses):
Expand Down Expand Up @@ -668,11 +667,11 @@ def collect_labels(newpasses, stats):
logger.info("Finished coordinated schedules.")


def run():
def run(args=None):
"""The schedule command."""
global logger

opts = parse_args()
opts = parse_args(args)

if opts.config:
# read_config() returns:
Expand Down Expand Up @@ -787,7 +786,7 @@ def setup_logging(opts):
logger = logging.getLogger("trollsched")


def parse_args():
def parse_args(args=None):
"""Parse arguments from the command line."""
parser = argparse.ArgumentParser()
# general arguments
Expand Down Expand Up @@ -849,7 +848,7 @@ def parse_args():
help="generate a MEOS schedule file")
group_outp.add_argument("--metno-xml", action="store_true",
help="generate a METNO xml pass data file")
opts = parser.parse_args()
opts = parser.parse_args(args)

if (not opts.config) and (not (opts.lon or opts.lat or opts.alt)):
parser.error("Coordinates must be provided in the absence of "
Expand Down
67 changes: 66 additions & 1 deletion trollsched/tests/test_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
from unittest.mock import patch

import pytest
import yaml

from trollsched.satpass import get_aqua_terra_dumps, get_metopa_passes, get_next_passes
from trollsched.schedule import build_filename, conflicting_passes, fermia, fermib
from trollsched.schedule import build_filename, conflicting_passes, fermia, fermib, run


class TestTools:
Expand Down Expand Up @@ -312,3 +313,67 @@ def test_get_metopa_passes(self, exists):
assert metopa_passes[0].seconds() == pytest.approx(487.512589, 1e-5)
assert (metopa_passes[0].uptime - datetime(2018, 12, 4, 9, 17, 48, 530484)).seconds == 0
assert (metopa_passes[0].risetime - datetime(2018, 12, 4, 9, 17, 21, 644605)).seconds == 0


euron1 = """euron1:
description: Northern Europe - 1km
projection:
proj: stere
ellps: WGS84
lat_0: 90.0
lon_0: 0.0
lat_ts: 60.0
shape:
height: 3072
width: 3072
area_extent:
lower_left_xy: [-1000000.0, -4500000.0]
upper_right_xy: [2072000.0, -1428000.0]
"""



def test_pyorbitals_platform_name(tmp_path):
"""Test that using pyorbital's platform name allows spurious names in the TLE data."""
spurious_tle = ("NOAA 20 (JPSS-1)\n"
"1 43013U 17073A 24093.57357837 .00000145 00000+0 86604-4 0 9999\n"
"2 43013 98.7039 32.7741 0007542 324.8026 35.2652 14.21254587330172\n")


config_file = tmp_path / "config.yaml"
tle_file = tmp_path / "test.tle"
area_file = tmp_path / "areas.yaml"
sched_file = tmp_path / "mysched.xml"

with open(area_file, "w") as fd:
fd.write(euron1)

with open(tle_file, "w") as fd:
fd.write(spurious_tle)


config = dict(default=dict(station=["nrk"],
forward=12,
start=0,
center_id="SMHI"),
stations=dict(nrk=dict(name="nrk",
longitude=16,
latitude=58,
altitude=0,
satellites=["noaa-20"],
area="euron1",
area_file=os.fspath(area_file))),

pattern=dict(dir_output=os.fspath(tmp_path),
file_xml=os.fspath(sched_file)),
satellites={"noaa-20": dict(schedule_name="noaa20",
international_designator="43013",
night=0.4,
day=0.9)}
)

with open(config_file, "w") as fd:
fd.write(yaml.dump(config))

run(["-c", os.fspath(config_file), "-x", "-t", os.fspath(tle_file)])
assert sched_file in tmp_path.iterdir()
3 changes: 2 additions & 1 deletion trollsched/writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ def generate_metno_xml_file(output_file, allpasses, coords, start, end, station_

def generate_xml_requests(sched, start, end, station_name, center_id, report_mode=False):
"""Create xml requests."""
import defusedxml.ElementTree as ET
# defusedxml is not needed here as we only generate an xml file (ie no reading of potentially harmful data)
from xml.etree import ElementTree as ET # noqa

reqtime = datetime.utcnow()
time_format = "%Y-%m-%d-%H:%M:%S"
Expand Down

0 comments on commit 0a9c19a

Please sign in to comment.