1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5from __future__ import absolute_import, division, print_function 

6 

7import logging 

8import os.path as op 

9import numpy as num 

10 

11from pyrocko import gf, util 

12from pyrocko.guts import Float 

13 

14from .base import TargetGenerator, NoiseGenerator 

15from ..station import RandomStationGenerator, StationGenerator 

16 

17DEFAULT_STORE_ID = 'ak135_static' 

18 

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

20guts_prefix = 'pf.scenario' 

21 

22 

23class GPSNoiseGenerator(NoiseGenerator): 

24 measurement_duarion_days = Float.T( 

25 default=2., 

26 help='Measurement duration in days') 

27 

28 def add_noise(self, campaign): 

29 # https://www.nat-hazards-earth-syst-sci.net/15/875/2015/nhess-15-875-2015.pdf 

30 waterlevel = 1. - (.99 + .0015 * self.measurement_duarion_days) # noqa 

31 logger.warning('GNSSNoiseGenerator is a work-in-progress!') 

32 

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

34 pass 

35 # rstate = self.get_rstate(ista) 

36 

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

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

39 

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

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

42 

43 

44class GNSSCampaignGenerator(TargetGenerator): 

45 station_generator = StationGenerator.T( 

46 default=RandomStationGenerator( 

47 network_name='GN', 

48 channels=None), 

49 help='The StationGenerator for creating the stations.') 

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 return self.station_generator.get_stations() 

62 

63 def get_targets(self): 

64 stations = self.get_stations() 

65 lats = num.array([s.lat for s in stations]) 

66 lons = num.array([s.lon for s in stations]) 

67 

68 target = gf.GNSSCampaignTarget( 

69 lats=lats, 

70 lons=lons, 

71 store_id=self.store_id) 

72 

73 return [target] 

74 

75 def get_gnss_campaigns(self, engine, sources, tmin=None, tmax=None): 

76 try: 

77 resp = engine.process( 

78 sources, 

79 self.get_targets(), 

80 nthreads=0) 

81 except gf.meta.OutOfBounds: 

82 logger.warning('Could not calculate GNSS displacements' 

83 ' - the GF store\'s extend is too small!') 

84 return [] 

85 

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

87 

88 stacked_campaign = campaigns[0] 

89 stacked_campaign.name = 'Scenario Campaign' 

90 for camp in campaigns[1:]: 

91 for ista, sta in enumerate(camp.stations): 

92 stacked_campaign.stations[ista].north.shift += sta.north.shift 

93 stacked_campaign.stations[ista].east.shift += sta.east.shift 

94 stacked_campaign.stations[ista].up.shift += sta.up.shift 

95 

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

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

98 

99 if self.noise_generator is not None: 

100 self.noise_generator.add_noise(stacked_campaign) 

101 

102 return [stacked_campaign] 

103 

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

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

106 util.ensuredir(path_gnss) 

107 

108 fn = op.join(path_gnss, 

109 'campaign-%s.yml' % self.station_generator.network_name) 

110 

111 if op.exists(fn): 

112 return 

113 

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

115 

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

117 for camp in campaigns: 

118 camp.dump(stream=f) 

119 

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

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