예제 #1
0
파일: main.go 프로젝트: GeoNet/delta
func main() {

	var source string
	flag.StringVar(&source, "source", "GeoNet", "stationxml source")

	var sender string
	flag.StringVar(&sender, "sender", "WEL(GNS_Test)", "stationxml sender")

	var module string
	flag.StringVar(&module, "module", "Delta", "stationxml module")

	var output string
	flag.StringVar(&output, "output", "-", "output xml file")

	var base string
	flag.StringVar(&base, "base", "../..", "base of delta files on disk")

	var network string
	flag.StringVar(&network, "network", "../../network", "base network directory")

	var install string
	flag.StringVar(&install, "install", "../../install", "base installs directory")

	var stationRegexp string
	flag.StringVar(&stationRegexp, "stations", "[A-Z0-9]+", "regex selection of stations")

	var stationList string
	flag.StringVar(&stationList, "station-list", "", "regex selection of stations from file")

	var channelRegexp string
	flag.StringVar(&channelRegexp, "channels", "[A-Z0-9]+", "regex selection of channels")

	var channelList string
	flag.StringVar(&channelList, "channel-list", "", "regex selection of channels from file")

	var networkRegexp string
	flag.StringVar(&networkRegexp, "networks", "[A-Z0-9]+", "regex selection of networks")

	var networkList string
	flag.StringVar(&networkList, "network-list", "", "regex selection of networks from file")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "Build a network StationXML file from delta meta & response information\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "Usage:\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "  %s [options]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		fmt.Fprintf(os.Stderr, "\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n")
	}

	flag.Parse()

	// load delta meta helper
	db, err := metadb.NewMetaDB(base)
	if err != nil {
		fmt.Fprintf(os.Stderr, "problem loading meta db %s: %v\n", base, err)
		os.Exit(1)
	}

	builder := Build{
		Networks: func() *regexp.Regexp {
			re, err := Matcher(networkList, networkRegexp)
			if err != nil {
				log.Fatalf("unable to compile network matcher: %v", err)
			}
			return re
		}(),
		Stations: func() *regexp.Regexp {
			re, err := Matcher(stationList, stationRegexp)
			if err != nil {
				log.Fatalf("unable to compile network matcher: %v", err)
			}
			return re
		}(),
		Channels: func() *regexp.Regexp {
			re, err := Matcher(channelList, channelRegexp)
			if err != nil {
				log.Fatalf("unable to compile network matcher: %v", err)
			}
			return re
		}(),
	}

	// build a representation of the network
	networks, err := builder.Construct(db)
	if err != nil {
		log.Fatalf("error: unable to build networks list: %v", err)
	}

	// render station xml
	root := stationxml.NewFDSNStationXML(source, sender, module, "", networks)
	if ok := root.IsValid(); ok != nil {
		log.Fatalf("error: invalid stationxml file")
	}

	// marshal into xml
	res, err := root.Marshal()
	if err != nil {
		log.Fatalf("error: unable to marshal stationxml: %v", err)
	}

	// output as needed ...
	switch output {
	case "-":
		fmt.Fprintln(os.Stdout, string(res))
	default:
		if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
			log.Fatalf("error: unable to create directory %s: %v", filepath.Dir(output), err)
		}
		if err := ioutil.WriteFile(output, res, 0644); err != nil {
			log.Fatalf("error: unable to write file %s: %v", output, err)
		}
	}
}
예제 #2
0
파일: main.go 프로젝트: ozym/delta
func main() {

	var verbose bool
	flag.BoolVar(&verbose, "verbose", false, "make noise")

	var source string
	flag.StringVar(&source, "source", "GeoNet", "stationxml source")

	var sender string
	flag.StringVar(&sender, "sender", "WEL(GNS_Test)", "stationxml sender")

	var module string
	flag.StringVar(&module, "module", "Delta", "stationxml module")

	var output string
	flag.StringVar(&output, "output", "-", "output xml file")

	var network string
	flag.StringVar(&network, "network", "../../network", "base network directory")

	var install string
	flag.StringVar(&install, "install", "../../install", "base installs directory")

	var stationRegexp string
	flag.StringVar(&stationRegexp, "stations", "[A-Z0-9]+", "regex selection of stations")

	var stationList string
	flag.StringVar(&stationList, "station-list", "", "regex selection of stations from file")

	var channelRegexp string
	flag.StringVar(&channelRegexp, "channels", "[A-Z0-9]+", "regex selection of channels")

	var channelList string
	flag.StringVar(&channelList, "channel-list", "", "regex selection of channels from file")

	var networkRegexp string
	flag.StringVar(&networkRegexp, "networks", "[A-Z0-9]+", "regex selection of networks")

	var networkList string
	flag.StringVar(&networkList, "network-list", "", "regex selection of networks from file")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "Build a network StationXML file from delta meta & response information\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "Usage:\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "  %s [options]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		fmt.Fprintf(os.Stderr, "\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n")
	}

	flag.Parse()

	if stationList != "" {
		s, err := loadRegexpList(stationList)
		if err != nil || s == nil {
			log.Fatalf("unable to load station list regexp %s: %v", stationList, err)
		}
		stationRegexp = string(s)
	}

	// which stations to process
	stationMatch, err := regexp.Compile(stationRegexp)
	if err != nil {
		log.Fatalf("unable to compile station regexp %s: %v", stationRegexp, err)
	}

	if channelList != "" {
		s, err := loadRegexpList(channelList)
		if err != nil || s == nil {
			log.Fatalf("unable to load channel list regexp %s: %v", channelList, err)
		}
		channelRegexp = string(s)
	}

	// which stations to process
	channelMatch, err := regexp.Compile(channelRegexp)
	if err != nil {
		log.Fatalf("unable to compile channel regexp %s: %v", channelRegexp, err)
	}

	if networkList != "" {
		s, err := loadRegexpList(networkList)
		if err != nil || s == nil {
			log.Fatalf("unable to load network list regexp %s: %v", networkList, err)
		}
		networkRegexp = string(s)
	}

	// which networks to process
	networkMatch, err := regexp.Compile(networkRegexp)
	if err != nil {
		log.Fatalf("unable to compile network regexp %s: %v", networkRegexp, err)
	}

	// load meta information
	metaData, err := NewMeta(network, install)
	if err != nil {
		log.Fatalf("unable to load meta data: %v", err)
	}

	var networks []stationxml.Network

	stas := make(map[string][]stationxml.Station)
	for _, sta := range metaData.GetStationKeys() {
		station := metaData.GetStation(sta)
		if station == nil {
			continue
		}

		if !stationMatch.MatchString(sta) {
			continue
		}

		if verbose {
			log.Printf("checking station: %s", sta)
		}

		net := metaData.GetNetwork(station.Network)
		if net == nil {
			continue
		}

		if !networkMatch.MatchString(net.External) {
			continue
		}

		var channels []stationxml.Channel

		for _, conn := range metaData.GetConnections(sta) {
			if verbose {
				log.Printf("checking station channel: %s %s", sta, conn.Location)
			}

			if metaData.GetStreams(sta, conn.Location) == nil {
				continue
			}

			deploys := metaData.GetDeploys(conn.Place)
			if deploys == nil {
				log.Printf("skipping station channel: %s %s [no deployed datalogger]", sta, conn.Place)
				continue
			}

			l := metaData.GetSite(sta, conn.Location)
			if l == nil {
				log.Printf("skipping station channel: %s %s [no site map]", sta, conn.Location)
				continue
			}

			for _, sensorInstall := range metaData.GetInstalls(sta) {
				if verbose {
					log.Printf("checking station sensor install: %s %s", sta, sensorInstall.Location)
				}
				switch {
				case sensorInstall.Location != conn.Location:
					continue
				case sensorInstall.Start.After(conn.End):
					continue
				case sensorInstall.End.Before(conn.Start):
					continue
				case sensorInstall.Start == conn.End:
					continue
				}
				for _, dataloggerDeploy := range deploys {
					if verbose {
						log.Printf("checking station datalogger deploys: %s %s %s", sta, conn.Place, dataloggerDeploy.Role)
					}
					switch {
					case dataloggerDeploy.Role != conn.Role:
						continue
					case dataloggerDeploy.Start.After(conn.End):
						continue
					case dataloggerDeploy.End.Before(conn.Start):
						continue
					case dataloggerDeploy.Start == conn.End:
						continue
					case dataloggerDeploy.Start.After(sensorInstall.End):
						continue
					case dataloggerDeploy.End.Before(sensorInstall.Start):
						continue
					case dataloggerDeploy.Start == sensorInstall.End:
						continue
					case sensorInstall.End == dataloggerDeploy.Start:
						continue
					}

					on := conn.Start
					if sensorInstall.Start.After(on) {
						on = sensorInstall.Start
					}
					if dataloggerDeploy.Start.After(on) {
						on = dataloggerDeploy.Start
					}
					off := conn.End
					if sensorInstall.End.Before(off) {
						off = sensorInstall.End
					}
					if dataloggerDeploy.End.Before(off) {
						off = dataloggerDeploy.End
					}

					if verbose {
						log.Printf("checking station response: %s %s %s", sta, dataloggerDeploy.Model, sensorInstall.Model)
					}
					for _, r := range GetResponseStreams(dataloggerDeploy.Model, sensorInstall.Model) {
						stream := metaData.GetStream(sta, conn.Location, r.Datalogger.SampleRate, on)
						if stream == nil {
							continue
						}

						var types []stationxml.Type
						for _, t := range r.Type {
							switch t {
							case 'c', 'C':
								types = append(types, stationxml.TypeContinuous)
							case 't', 'T':
								types = append(types, stationxml.TypeTriggered)
							case 'g', 'G':
								types = append(types, stationxml.TypeGeophysical)
							case 'w', 'W':
								types = append(types, stationxml.TypeWeather)
							}
						}

						labels := r.Channels
						if stream.Axial {
							labels = strings.Replace(labels, "N", "1", -1)
							labels = strings.Replace(labels, "E", "2", -1)
						} else {
							labels = strings.Replace(labels, "1", "N", -1)
							labels = strings.Replace(labels, "2", "E", -1)
						}

						model, ok := SensorModels[sensorInstall.Model]
						if !ok {
							continue
						}

						freq := r.Datalogger.Frequency
						for n := 0; n < len(labels) && n < len(model.Components); n++ {
							cha, comp := labels[n], model.Components[n]

							if !channelMatch.MatchString(r.Label + string(cha)) {
								continue
							}

							dip := comp.Dip
							azimuth := sensorInstall.Azimuth + comp.Azimuth

							// only rotate horizontal components
							if dip == 0.0 {
								if r.Sensor.Reversed {
									azimuth += 180.0
								}
								if r.Datalogger.Reversed {
									azimuth += 180.0
								}
								if stream.Reversed {
									azimuth += 180.0
								}
								// avoid negative zero
								dip = 0.0
							} else {
								if r.Sensor.Reversed {
									dip *= -1.0
								}
								if r.Datalogger.Reversed {
									dip *= -1.0
								}
								if stream.Reversed {
									dip *= -1.0
								}
							}

							// bring into positive range
							for azimuth < 0.0 {
								azimuth += 360.0
							}
							for azimuth > 360.0 {
								azimuth -= 360.0
							}

							tag := fmt.Sprintf("%s.%s.%s%c", sta, l.Location, r.Label, cha)

							var stages []stationxml.ResponseStage
							for _, s := range append(r.Sensor.Stages, r.Datalogger.Stages...) {
								if s.StageSet == nil {
									continue
								}
								stages = append(stages, s.StageSet.ResponseStage(Stage{
									responseStage: s,
									count:         len(stages) + 1,
									id:            s.Filter,
									name:          fmt.Sprintf("%s.%04d.%03d.stage_%d", tag, on.Year(), on.YearDay(), len(stages)+1),
									frequency:     freq,
								}))
							}

							channels = append(channels, stationxml.Channel{
								BaseNode: stationxml.BaseNode{
									Code:      r.Label + string(cha),
									StartDate: &stationxml.DateTime{on},
									EndDate:   &stationxml.DateTime{off},
									RestrictedStatus: func() stationxml.RestrictedStatus {
										switch net.Restricted {
										case true:
											return stationxml.StatusClosed
										default:
											return stationxml.StatusOpen
										}
									}(),
									Comments: []stationxml.Comment{
										stationxml.Comment{
											Id: 1,
											Value: func() string {
												switch l.Survey {
												case "Unknown":
													return "Location estimation method is unknown"
												case "External GPS Device":
													return "Location estimated from external GPS measurement"
												case "Internal GPS Clock":
													return "Location estimated from internal GPS clock"
												case "Topographic Map":
													return "Location estimated from topographic map"
												case "Site Survey":
													return "Location estimated from plans and survey to mark"
												default:
													return "Location estimation method is unknown"
												}
											}(),
										},
										stationxml.Comment{
											Id:    2,
											Value: "Location is given in " + l.Datum,
										},
										stationxml.Comment{
											Id:    3,
											Value: "Sensor orientation not known",
										},
									},
								},
								LocationCode: l.Location,
								Latitude: stationxml.Latitude{
									LatitudeBase: stationxml.LatitudeBase{
										Float: stationxml.Float{
											Value: l.Latitude,
										},
									},
									Datum: l.Datum,
								},
								Longitude: stationxml.Longitude{
									LongitudeBase: stationxml.LongitudeBase{
										Float: stationxml.Float{
											Value: l.Longitude,
										},
									},
									Datum: l.Datum,
								},
								Elevation: stationxml.Distance{Float: stationxml.Float{Value: l.Elevation}},
								Depth:     stationxml.Distance{Float: stationxml.Float{Value: -sensorInstall.Vertical}},
								Azimuth:   &stationxml.Azimuth{Float: stationxml.Float{Value: azimuth}},
								Dip:       &stationxml.Dip{Float: stationxml.Float{Value: dip}},
								Types:     types,
								SampleRateGroup: stationxml.SampleRateGroup{
									SampleRate: stationxml.SampleRate{Float: stationxml.Float{Value: r.SampleRate}},
									SampleRateRatio: func() *stationxml.SampleRateRatio {
										if r.SampleRate > 1.0 {
											return &stationxml.SampleRateRatio{
												NumberSamples: int32(r.SampleRate),
												NumberSeconds: 1,
											}
										} else {
											return &stationxml.SampleRateRatio{
												NumberSamples: 1,
												NumberSeconds: int32(1.0 / r.SampleRate),
											}
										}
									}(),
								},
								StorageFormat: r.StorageFormat,
								ClockDrift:    &stationxml.ClockDrift{Float: stationxml.Float{Value: r.ClockDrift}},
								Sensor: &stationxml.Equipment{
									ResourceId: "Sensor#" + sensorInstall.Model + ":" + sensorInstall.Serial,
									Type: func() string {
										if t, ok := SensorModels[sensorInstall.Model]; ok {
											return t.Type
										}
										return ""
									}(),
									Description: func() string {
										if t, ok := SensorModels[sensorInstall.Model]; ok {
											return t.Description
										}
										return ""
									}(),
									Manufacturer: func() string {
										if t, ok := SensorModels[sensorInstall.Model]; ok {
											return t.Manufacturer
										}
										return ""
									}(),
									Vendor: func() string {
										if t, ok := SensorModels[sensorInstall.Model]; ok {
											return t.Vendor
										}
										return ""
									}(),
									Model:        sensorInstall.Model,
									SerialNumber: sensorInstall.Serial,
									InstallationDate: func() *stationxml.DateTime {
										return &stationxml.DateTime{sensorInstall.Start}
									}(),
									RemovalDate: func() *stationxml.DateTime {
										if time.Now().After(sensorInstall.End) {
											return &stationxml.DateTime{sensorInstall.End}
										}
										return nil
									}(),
								},

								DataLogger: &stationxml.Equipment{
									ResourceId: "Datalogger#" + dataloggerDeploy.Model + ":" + dataloggerDeploy.Serial,
									Type: func() string {
										if t, ok := DataloggerModels[dataloggerDeploy.Model]; ok {
											return t.Type
										}
										return ""
									}(),
									Description: func() string {
										if t, ok := DataloggerModels[dataloggerDeploy.Model]; ok {
											return t.Description
										}
										return ""
									}(),
									Manufacturer: func() string {
										if t, ok := DataloggerModels[dataloggerDeploy.Model]; ok {
											return t.Manufacturer
										}
										return ""
									}(),
									Vendor: func() string {
										if t, ok := DataloggerModels[dataloggerDeploy.Model]; ok {
											return t.Vendor
										}
										return ""
									}(),
									Model:        dataloggerDeploy.Model,
									SerialNumber: dataloggerDeploy.Serial,
									InstallationDate: func() *stationxml.DateTime {
										return &stationxml.DateTime{dataloggerDeploy.Start}
									}(),
									RemovalDate: func() *stationxml.DateTime {
										if time.Now().After(dataloggerDeploy.End) {
											return &stationxml.DateTime{dataloggerDeploy.End}
										}
										return nil
									}(),
								},
								Response: &stationxml.Response{
									Stages: stages,
									InstrumentSensitivity: &stationxml.Sensitivity{
										//TODO: check we may need to adjust gain for different frequency
										Gain: stationxml.Gain{
											Value: func() float64 {
												var gain float64 = 1.0
												for _, s := range stages {
													gain *= s.StageGain.Value
												}
												return gain
											}(),
											Frequency: freq,
										},
										InputUnits: func() stationxml.Units {
											var units stationxml.Units
											if len(stages) > 0 {
												s := stages[0]
												switch {
												case s.PolesZeros != nil:
													units = s.PolesZeros.BaseFilter.InputUnits
												case s.Coefficients != nil:
													units = s.Coefficients.BaseFilter.InputUnits
												case s.ResponseList != nil:
													units = s.ResponseList.BaseFilter.InputUnits
												case s.FIR != nil:
													units = s.FIR.BaseFilter.InputUnits
												case s.Polynomial != nil:
													units = s.Polynomial.BaseFilter.InputUnits
												}
											}
											return units
										}(),
										OutputUnits: func() stationxml.Units {
											var units stationxml.Units
											if len(stages) > 0 {
												s := stages[len(stages)-1]
												switch {
												case s.PolesZeros != nil:
													units = s.PolesZeros.BaseFilter.OutputUnits
												case s.Coefficients != nil:
													units = s.Coefficients.BaseFilter.OutputUnits
												case s.ResponseList != nil:
													units = s.ResponseList.BaseFilter.OutputUnits
												case s.FIR != nil:
													units = s.FIR.BaseFilter.OutputUnits
												case s.Polynomial != nil:
													units = s.Polynomial.BaseFilter.OutputUnits
												}
											}
											return units
										}(),
									},
								},
							})

						}
					}
				}
			}
		}

		sort.Sort(Channels(channels))

		stas[net.External] = append(stas[net.External], stationxml.Station{
			BaseNode: stationxml.BaseNode{
				Code:        station.Code,
				Description: net.Description,
				RestrictedStatus: func() stationxml.RestrictedStatus {
					switch net.Restricted {
					case true:
						return stationxml.StatusClosed
					default:
						return stationxml.StatusOpen
					}
				}(),
				StartDate: &(stationxml.DateTime{station.Start}),
				EndDate:   &(stationxml.DateTime{station.End}),
				Comments: []stationxml.Comment{
					stationxml.Comment{
						Id:    1,
						Value: "Location is given in " + station.Datum,
					},
				},
			},
			Latitude: stationxml.Latitude{LatitudeBase: stationxml.LatitudeBase{
				Float: stationxml.Float{
					Value: station.Latitude,
				}}, Datum: station.Datum},
			Longitude: stationxml.Longitude{LongitudeBase: stationxml.LongitudeBase{
				Float: stationxml.Float{
					Value: station.Longitude,
				}}, Datum: station.Datum},
			Elevation: stationxml.Distance{
				Float: stationxml.Float{Value: station.Elevation},
			},
			Site: stationxml.Site{
				Name: func() string {
					if station.Name != "" {
						return station.Name
					} else {
						return station.Code
					}
				}(),
				Description: func() string {
					place := Place{
						Latitude:  station.Latitude,
						Longitude: station.Longitude,
					}
					if loc := Locations.Closest(place); loc != nil {
						if dist := loc.Distance(place); dist < 5.0 {
							return fmt.Sprintf("within 5 km of %s", loc.Name)
						} else {
							return fmt.Sprintf("%.0f km %s of %s", dist, loc.Compass(place), loc.Name)
						}
					}
					return ""
				}(),
			},
			CreationDate: stationxml.DateTime{station.Start},
			TerminationDate: func() *stationxml.DateTime {
				if time.Now().Before(station.End) {
					return nil
				}
				return &stationxml.DateTime{station.End}
			}(),
			Channels: channels,
		})
	}

	for networkCode, stationList := range stas {
		var net *meta.Network
		if net = metaData.GetNetwork(networkCode); net == nil {
			continue
		}

		var on, off *stationxml.DateTime
		for _, s := range stationList {
			if s.BaseNode.StartDate != nil {
				if on == nil || s.BaseNode.StartDate.Before(on.Time) {
					on = s.BaseNode.StartDate
				}
			}
			if s.BaseNode.EndDate != nil {
				if off == nil || s.BaseNode.EndDate.After(off.Time) {
					off = s.BaseNode.EndDate
				}
			}
		}
		networks = append(networks, stationxml.Network{
			BaseNode: stationxml.BaseNode{
				Code:        net.External,
				Description: net.Description,
				RestrictedStatus: func() stationxml.RestrictedStatus {
					switch net.Restricted {
					case true:
						return stationxml.StatusClosed
					default:
						return stationxml.StatusOpen
					}
				}(),
				StartDate: on,
				EndDate:   off,
			},
			SelectedNumberStations: uint32(len(stationList)),
			Stations:               stationList,
		})
	}

	// render station xml
	root := stationxml.NewFDSNStationXML(source, sender, module, "", networks)
	if ok := root.IsValid(); ok != nil {
		log.Fatal("error: invalid stationxml file")
	}

	// marshal into xml
	x, err := root.Marshal()
	if err != nil {
		log.Fatalf("error: unable to marshal stationxml: %v", err)
	}

	// output as needed ...
	switch output {
	case "-":
		fmt.Fprintln(os.Stdout, string(x))
	default:
		if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
			log.Fatalf("error: unable to create directory %s: %v", filepath.Dir(output), err)
		}
		if err := ioutil.WriteFile(output, x, 0644); err != nil {
			log.Fatalf("error: unable to write file %s: %v", output, err)
		}
	}
}