1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import logging
7import os.path as op
8import numpy as num
10from pyrocko import gf, util
11from pyrocko.guts import Float, List
13from .base import TargetGenerator, NoiseGenerator
14from ..station import RandomStationGenerator, StationGenerator
16DEFAULT_STORE_ID = 'ak135_static'
18logger = logging.getLogger('pyrocko.scenario.targets.gnss_campaign')
19guts_prefix = 'pf.scenario'
22class GPSNoiseGenerator(NoiseGenerator):
23 measurement_duarion_days = Float.T(
24 default=2.,
25 help='Measurement duration in days')
27 def add_noise(self, campaign):
28 # https://www.nat-hazards-earth-syst-sci.net/15/875/2015/nhess-15-875-2015.pdf
29 waterlevel = 1. - (.99 + .0015 * self.measurement_duarion_days) # noqa
30 logger.warning('GPSNoiseGenerator is a work-in-progress!')
32 for ista, sta in enumerate(campaign.stations):
33 pass
34 # rstate = self.get_rstate(ista)
36 # sta.north.sigma = 8e-3
37 # sta.east.sigma = 8e-3
39 # sta.north.shift += rstate.normal(0., sta.north.sigma)
40 # sta.east.shift += rstate.normal(0., sta.east.sigma)
43class GNSSCampaignGenerator(TargetGenerator):
44 station_generators = List.T(
45 StationGenerator.T(),
46 default=[RandomStationGenerator.D(
47 network_name='GN',
48 channels=None)],
49 help='The StationGenerator.')
51 noise_generator = NoiseGenerator.T(
52 default=GPSNoiseGenerator.D(),
53 optional=True,
54 help='Add Synthetic noise to the GNSS displacements.')
56 store_id = gf.StringID.T(
57 default=DEFAULT_STORE_ID,
58 help='The GF store to use for forward-calculations.')
60 def get_stations(self):
61 stations = []
62 for station_generator in self.station_generators:
63 stations.extend(station_generator.get_stations())
64 return stations
66 def get_targets(self):
67 stations = self.get_stations()
68 lats = num.array([s.effective_lat for s in stations])
69 lons = num.array([s.effective_lon for s in stations])
71 target = gf.GNSSCampaignTarget(
72 lats=lats,
73 lons=lons,
74 store_id=self.store_id)
76 return [target]
78 def get_gnss_campaigns(self, engine, sources, tmin=None, tmax=None):
79 try:
80 resp = engine.process(
81 sources,
82 self.get_targets(),
83 nthreads=0)
84 except gf.meta.OutOfBounds:
85 logger.warning('Could not calculate GNSS displacements'
86 ' - the GF store\'s extend is too small!')
87 return []
89 campaigns = [r.campaign for r in resp.static_results()]
91 stacked_campaign = campaigns[0]
92 stacked_campaign.name = 'Scenario Campaign'
93 for camp in campaigns[1:]:
94 for ista, sta in enumerate(camp.stations):
95 stacked_campaign.stations[ista].north.shift += sta.north.shift
96 stacked_campaign.stations[ista].east.shift += sta.east.shift
97 stacked_campaign.stations[ista].up.shift += sta.up.shift
99 for ista, sta in enumerate(stacked_campaign.stations):
100 sta.code = 'SY%02d' % (ista + 1)
102 if self.noise_generator is not None:
103 self.noise_generator.add_noise(stacked_campaign)
105 return [stacked_campaign]
107 def ensure_data(self, engine, sources, path, tmin=None, tmax=None):
108 path_gnss = op.join(path, 'gnss')
109 util.ensuredir(path_gnss)
111 networks = []
112 for sg in self.station_generators:
113 try:
114 networks.append(sg.network_name)
115 except AttributeError:
116 pass
118 fn = op.join(
119 path_gnss,
120 'campaign-%s.yml' % '_'.join(networks))
122 if op.exists(fn):
123 return
125 campaigns = self.get_gnss_campaigns(engine, sources, tmin, tmax)
127 with open(fn, 'w') as f:
128 for camp in campaigns:
129 camp.dump(stream=f)
131 def add_map_artists(self, engine, sources, automap):
132 automap.add_gnss_campaign(self.get_gnss_campaigns(engine, sources)[0])