1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6import logging 

7import os.path as op 

8import numpy as num 

9 

10from pyrocko import gf, util 

11from pyrocko.guts import Float, List 

12 

13from .base import TargetGenerator, NoiseGenerator 

14from ..station import RandomStationGenerator, StationGenerator 

15 

16DEFAULT_STORE_ID = 'ak135_static' 

17 

18logger = logging.getLogger('pyrocko.scenario.targets.gnss_campaign') 

19guts_prefix = 'pf.scenario' 

20 

21 

22class GPSNoiseGenerator(NoiseGenerator): 

23 measurement_duarion_days = Float.T( 

24 default=2., 

25 help='Measurement duration in days') 

26 

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!') 

31 

32 for ista, sta in enumerate(campaign.stations): 

33 pass 

34 # rstate = self.get_rstate(ista) 

35 

36 # sta.north.sigma = 8e-3 

37 # sta.east.sigma = 8e-3 

38 

39 # sta.north.shift += rstate.normal(0., sta.north.sigma) 

40 # sta.east.shift += rstate.normal(0., sta.east.sigma) 

41 

42 

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.') 

50 

51 noise_generator = NoiseGenerator.T( 

52 default=GPSNoiseGenerator.D(), 

53 optional=True, 

54 help='Add Synthetic noise to the GNSS displacements.') 

55 

56 store_id = gf.StringID.T( 

57 default=DEFAULT_STORE_ID, 

58 help='The GF store to use for forward-calculations.') 

59 

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 

65 

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]) 

70 

71 target = gf.GNSSCampaignTarget( 

72 lats=lats, 

73 lons=lons, 

74 store_id=self.store_id) 

75 

76 return [target] 

77 

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 [] 

88 

89 campaigns = [r.campaign for r in resp.static_results()] 

90 

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 

98 

99 for ista, sta in enumerate(stacked_campaign.stations): 

100 sta.code = 'SY%02d' % (ista + 1) 

101 

102 if self.noise_generator is not None: 

103 self.noise_generator.add_noise(stacked_campaign) 

104 

105 return [stacked_campaign] 

106 

107 def ensure_data(self, engine, sources, path, tmin=None, tmax=None): 

108 path_gnss = op.join(path, 'gnss') 

109 util.ensuredir(path_gnss) 

110 

111 networks = [] 

112 for sg in self.station_generators: 

113 try: 

114 networks.append(sg.network_name) 

115 except AttributeError: 

116 pass 

117 

118 fn = op.join( 

119 path_gnss, 

120 'campaign-%s.yml' % '_'.join(networks)) 

121 

122 if op.exists(fn): 

123 return 

124 

125 campaigns = self.get_gnss_campaigns(engine, sources, tmin, tmax) 

126 

127 with open(fn, 'w') as f: 

128 for camp in campaigns: 

129 camp.dump(stream=f) 

130 

131 def add_map_artists(self, engine, sources, automap): 

132 automap.add_gnss_campaign(self.get_gnss_campaigns(engine, sources)[0])