Пример #1
0
/*!
 \brief  Save the VS Magnitude in the preferred origin of the event

 Update the preferred origin object of the event and send a message
 to the master so the magnitude can be saved to the database.

 \param event The sc3 event to which the magnitude belongs
 \param mag   The magnitude to be added to the event
 \param stacnt Station Count (number of stations used to calculate the magnitude)
 */
void VsMagnitude::updateVSMagnitude(Event *event, VsEvent *vsevt) {
    SEISCOMP_DEBUG(
        "%s: %s: VS = %.4f", event->publicID().c_str(), _currentTime.toString("%FT%T.%fZ").c_str(), *vsevt->vsMagnitude);
    SEISCOMP_DEBUG("Update number is %d", vsevt->update);

    OriginPtr org = _cache.get<Origin>(event->preferredOriginID());
    if ( org == NULL ) {
        SEISCOMP_WARNING("Object %s not found in cache\nIs the cache size big enough?\n"
                         "Have you subscribed to all necessary message groups?",
                         event->preferredOriginID().c_str());
        return;
    }

    Notifier::SetEnabled(true);
    _creationInfo.setCreationTime(_currentTime);
    _creationInfo.setModificationTime(Core::None);
    _creationInfo.setVersion(Core::toString(vsevt->update));
    MagnitudePtr nmag = Magnitude::Create();
    nmag->setMagnitude(RealQuantity(*vsevt->vsMagnitude));
    nmag->setType("MVS");
    nmag->setStationCount(vsevt->vsStationCount);
    nmag->setCreationInfo(_creationInfo);
    org->add(nmag.get());
    for (StaMagArray::iterator it = vsevt->staMags.begin(); it != vsevt->staMags.end(); ++it) {
        const DataModel::StationMagnitude *staMag = (*it).get();
        nmag->add(new StationMagnitudeContribution(staMag->publicID(),staMag->magnitude().value()- *vsevt->vsMagnitude,1.0));
    }
    vsevt->staMags.clear();

    /// set a comment containing the update number
    /// if the update numbers of two successive comments for the
    /// same event are identical signals the logging script that
    /// the event processing has finished
    setComments(nmag.get(), "update", vsevt->update);

    /// set the likelihood of the estimate as a comment
    setComments(nmag.get(), "likelihood", vsevt->likelihood);

    /// send the message containing the vs magnitude and the
    /// two comments to scmaster
    Core::MessagePtr msg = Notifier::GetMessage();
    if ( connection() && msg )
        connection()->send(msg.get());
    Notifier::SetEnabled(false);
}
Пример #2
0
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
bool AmpTool::run() {
	if ( !_originID.empty() ) {
		OriginPtr org = Origin::Cast(query()->getObject(Origin::TypeInfo(), _originID));
		if ( !org ) {
			cerr << "Origin not found!" << endl;
			return false;
		}

		_fetchMissingAmplitudes = false;
		query()->loadArrivals(org.get());
		process(org.get());
		return true;
	}

	if ( !_strTimeWindowStartTime.empty() || !_strTimeWindowEndTime.empty() ) {
		if ( database() == NULL ) {
			cerr << "No database currently active for time window reprocessing" << endl;
			return false;
		}

		Core::Time startTime, endTime;

		if ( !_strTimeWindowStartTime.empty()
		  && !startTime.fromString(_strTimeWindowStartTime.c_str(), "%F %T") ) {
			cerr << "Invalid start time: " << _strTimeWindowStartTime << endl;
			return false;
		}

		if ( !_strTimeWindowEndTime.empty()
		  && !endTime.fromString(_strTimeWindowEndTime.c_str(), "%F %T") ) {
			cerr << "Invalid end time: " << _strTimeWindowEndTime << endl;
			return false;
		}

		std::string dbQuery;
		dbQuery += "select PPick." + _T("publicID") + ", Pick.* from Pick,PublicObject as PPick,Amplitude "
		           "where Pick._oid=PPick._oid and Amplitude." + _T("pickID") + "=PPick." + _T("publicID");

		if ( startTime.valid() )
			 dbQuery += " and Pick." + _T("time_value") + ">='" + startTime.toString("%F %T") + "'";

		if ( endTime.valid() )
			 dbQuery += " and Pick." + _T("time_value") + "<'" + endTime.toString("%F %T") + "'";

		dbQuery += " group by Amplitude." + _T("pickID");

		if ( !commandline().hasOption("commit") )
			_testMode = true;

		EventParametersPtr ep;
		if ( _testMode )
			ep = new EventParameters;

		typedef list<PickPtr> PickList;
		PickList picks;

		cerr << "Collecting picks ... " << flush;
		DatabaseIterator db_it = query()->getObjectIterator(dbQuery, Pick::TypeInfo());
		ObjectPtr obj;
		while ( obj = db_it.get() ) {
			Pick *pick = static_cast<Pick*>(obj.get());
			try {
				pick->waveformID().networkCode();
				pick->waveformID().stationCode();
				pick->waveformID().locationCode();
				pick->waveformID().channelCode();

				pick->time().value();
			}
			catch ( ... ) {
				continue;
			}

			++db_it;

			picks.push_back(pick);
			if ( ep ) ep->add(pick);
		}

		db_it.close();

		cerr << picks.size() << endl;

		_report << std::endl;
		_report << "Reprocessing report" << std::endl;
		_report << "-------------------" << std::endl;

		_report << " + Picks" << std::endl;

		int errors = 0;
		int ampsRecomputed = 0;
		int messagesSent = 0;

		int idx = 1;
		for ( PickList::iterator it = picks.begin(); it != picks.end(); ++it, ++idx ) {
			PickPtr pick = *it;
			SingleAmplitudeMap dbAmps;

			if ( isExitRequested() ) break;

			// Clear all processors
			_processors.clear();

			// Clear all station time windows
			_stationRequests.clear();

			_report << "   + " << pick->publicID() << std::endl;
			cerr << "[" << idx << "]" << " " << (*it)->publicID() << endl;
			db_it = query()->getAmplitudesForPick((*it)->publicID());
			while ( obj = db_it.get() ) {
				Amplitude *amp = static_cast<Amplitude*>(obj.get());
				cerr << "  [" << setw(10) << left << amp->type() << "]  ";

				AmplitudeProcessorPtr proc = AmplitudeProcessorFactory::Create(amp->type().c_str());
				if ( !proc ) {
					if ( _amplitudeTypes.find(amp->type()) == _amplitudeTypes.end() )
						cerr << "No processor";
					else {
						cerr << "No processor but enabled";
						++errors;
					}
				}
				else {
					cerr << "Fetch data";
					dbAmps[amp->type()] = amp;
					proc->setTrigger(pick->time().value());
					proc->setReferencingPickID(pick->publicID());
					proc->setPublishFunction(boost::bind(&AmpTool::storeLocalAmplitude, this, _1, _2));
					_report << "     + Data" << std::endl;
					addProcessor(proc.get(), pick.get(), None, None);
				}

				cerr << endl;
				++db_it;
			}

			db_it.close();

			cerr << "  --------------------------------" << endl;

			if ( _stationRequests.empty() ) continue;

			for ( RequestMap::iterator it = _stationRequests.begin(); it != _stationRequests.end(); ++it ) {
				StationRequest &req = it->second;
				for ( WaveformIDSet::iterator wit = req.streams.begin(); wit != req.streams.end(); ++wit ) {
					const WaveformStreamID &wsid = *wit;
					recordStream()->addStream(wsid.networkCode(), wsid.stationCode(),
					                          wsid.locationCode(), wsid.channelCode(),
					                          req.timeWindow.startTime(),
					                          req.timeWindow.endTime());
				}

				_report << " + TimeWindow (" << it->first << "): " << req.timeWindow.startTime().toString("%F %T")
				        << ", " << req.timeWindow.endTime().toString("%F %T") << std::endl;
			}

			_reprocessMap.clear();
			readRecords(false);

			list<AmplitudePtr> updates;

			for ( AmplitudeMap::iterator it = dbAmps.begin();
			      it != dbAmps.end(); ++it ) {
				AmplitudePtr oldAmp = it->second;
				AmplitudePtr newAmp = _reprocessMap[oldAmp->type()];

				cerr << "  [" << setw(10) << left << oldAmp->type() << "]  " << oldAmp->amplitude().value() << "  ";
				if ( newAmp ) {
					if ( newAmp->amplitude().value() != oldAmp->amplitude().value() ) {
						*oldAmp = *newAmp;
						if ( ep )
							ep->add(oldAmp.get());
						else
							updates.push_back(oldAmp);
						cerr << "->  " << newAmp->amplitude().value();
					}
					else
						cerr << "  no changes";

					++ampsRecomputed;
				}
				else {
					cerr << "-";
					++errors;
				}
				cerr << endl;
			}

			if ( !updates.empty() ) {
				if ( !_testMode ) {
					NotifierMessagePtr nmsg = new NotifierMessage;
					for ( list<AmplitudePtr>::iterator it = updates.begin();
					      it != updates.end(); ++it ) {
						nmsg->attach(new Notifier("EventParameters", OP_UPDATE, it->get()));
					}

					connection()->send(nmsg.get());
					++messagesSent;

					if ( messagesSent % 100 == 0 )
						sync();
				}
				else {
					cerr << "  --------------------------------" << endl;
					cerr << "  Test mode, nothing sent" << endl;
				}
			}
		}

		if ( ep ) {
			IO::XMLArchive ar;
			ar.create("-");
			ar.setFormattedOutput(true);
			ar << ep;
			ar.close();
		}

		cerr << "----------------------------------" << endl;
		cerr << "Recomputed " << ampsRecomputed << " amplitudes" << endl;
		cerr << "Sent " << messagesSent << " messages" << endl;
		if ( errors )
			cerr << errors << " errors occurred, check the processing log" << endl;

		return true;
	}


	if ( !_epFile.empty() ) {
		_fetchMissingAmplitudes = false;

		// Disable database
		setDatabase(NULL);
		_cache.setDatabaseArchive(NULL);

		IO::XMLArchive ar;
		if ( !ar.open(_epFile.c_str()) ) {
			SEISCOMP_ERROR("Failed to open %s", _epFile.c_str());
			return false;
		}

		ar >> _ep;
		ar.close();

		if ( !_ep ) {
			SEISCOMP_ERROR("No event parameters found in %s", _epFile.c_str());
			return false;
		}

		if ( commandline().hasOption("reprocess") ) {
			for ( size_t i = 0; i < _ep->amplitudeCount(); ++i ) {
				AmplitudePtr amp = _ep->amplitude(i);
				feed(amp.get());
			}
		}

		for ( size_t i = 0; i < _ep->originCount(); ++i ) {
			OriginPtr org = _ep->origin(i);
			SEISCOMP_INFO("Processing origin %s", org->publicID().c_str());
			process(org.get());
		}

		ar.create("-");
		ar.setFormattedOutput(true);
		ar << _ep;
		ar.close();

		_ep = NULL;

		return true;
	}
Пример #3
0
/*!
 \brief Process one event

 This is called by processEvents() when iterating over all events in the cache
 */
void VsMagnitude::process(VsEvent *evt, Event *event) {
    if ( evt->stations.empty() )
        return;
    Client::Inventory *inv = Client::Inventory::Instance();

    double stmag;
    double distdg, epicdist, azi1, azi2;
    WaveformStreamID wid;
    ReturnCode ret;
    Timeline::StationList unused;
    evt->allThresholdStationsCount = 0;
    vs.seteqlat(evt->lat);
    vs.seteqlon(evt->lon);

    vector<VsInput> inputs;

    SEISCOMP_LOG(_processingInfoChannel,
                 "Start logging for event: %s", event->publicID().c_str());
    SEISCOMP_LOG(_processingInfoChannel, "update number: %d", evt->update);

    OriginPtr org = _cache.get<Origin>(event->preferredOriginID());
    if ( !org ) {
        SEISCOMP_WARNING("Object %s not found in cache\nIs the cache size big enough?\n"
                         "Have you subscribed to all necessary message groups?",
                         event->preferredOriginID().c_str());
        return;
    }

    evt->staMags.clear();
    VsWindows::iterator it;
    for ( it = evt->stations.begin(); it != evt->stations.end(); ++it ) {
        Envelope venv, henv;
        Core::Time vtime, htime;
        string locationCode, channelCode;


        ret = _timeline.maxmimum(it->first, it->second.startTime(),
                                 it->second.endTime(), it->second.pickTime(), venv, vtime, henv,
                                 htime, locationCode, channelCode);

        if ( no_data == ret ) {
            SEISCOMP_WARNING("No data available for %s.%s.%s", it->first.first.c_str(),
                             it->first.second.c_str(),locationCode.c_str());
            unused.insert(it->first);
            continue;
        }

        DataModel::SensorLocation *loc;
        loc = inv->getSensorLocation(it->first.first, it->first.second,
                                     locationCode, it->second.pickTime());
        if ( loc == NULL ) {
            SEISCOMP_WARNING(
                "%s.%s.%s: sensor location not in inventory: ignoring", it->first.first.c_str(), it->first.second.c_str(), locationCode.c_str());
            continue;
        }

        Math::Geo::delazi(evt->lat, evt->lon, loc->latitude(), loc->longitude(),
                          &distdg, &azi1, &azi2);
        epicdist = Math::Geo::deg2km(distdg);

        // if data is clipped or not enough to use it for magnitude
        // computation add the station to the overall count and then continue
        if ( not_enough_data == ret || clipped_data == ret) {
            SEISCOMP_WARNING("Not enough data available for %s.%s.%s", it->first.first.c_str(),
                             it->first.second.c_str(),locationCode.c_str());
            unused.insert(it->first);
            continue;
        }

        // catch remaining errors
        if ( index_error == ret || undefined_problem == ret)
            continue;

        if ( _maxepicdist > 0 ) {
            if( epicdist > _maxepicdist )
                continue;
        }

        inputs.resize(inputs.size() + 1);
        VsInput &input = inputs.back();
        input.lat = (float) loc->latitude();
        input.lon = (float) loc->longitude();

        SoilClass soilClass;
        float ca = siteEffect(input.lat, input.lon,
                              std::max(venv.values[Acceleration], henv.values[Acceleration]),
                              Acceleration, soilClass);
        float cv = siteEffect(input.lat, input.lon,
                              std::max(venv.values[Velocity], henv.values[Velocity]),
                              Velocity, soilClass);
        float cd = siteEffect(input.lat, input.lon,
                              std::max(venv.values[Displacement], henv.values[Displacement]),
                              Displacement, soilClass);

        // Convert from m to cm and apply site effect correction
        input.ZA = (float) (venv.values[Acceleration] / ca) * 100;
        input.ZV = (float) (venv.values[Velocity] / cv) * 100;
        input.ZD = (float) (venv.values[Displacement] / cd) * 100;

        input.HA = (float) (henv.values[Acceleration] / ca) * 100;
        input.HV = (float) (henv.values[Velocity] / cv) * 100;
        input.HD = (float) (henv.values[Displacement] / cd) * 100;

        input.PSclass =
            vs.psclass(input.ZA, input.ZV, input.HA, input.HV) == 0 ?
            P_Wave : S_Wave;
        input.SOILclass = soilClass;

        input.mest = vs.mest(vs.ground_motion_ratio(input.ZA, input.ZD),
                             input.PSclass);

        // Record single station magnitudes
        Notifier::SetEnabled(true);
        _creationInfo.setCreationTime(_currentTime);
        _creationInfo.setModificationTime(Core::None);
        DataModel::StationMagnitudePtr staMag = DataModel::StationMagnitude::Create();
        staMag->setMagnitude(RealQuantity(input.mest));
        staMag->setType("MVS");
        staMag->setCreationInfo(_creationInfo);
        wid.setNetworkCode(it->first.first);
        wid.setStationCode(it->first.second);
        wid.setLocationCode(locationCode);
        wid.setChannelCode(channelCode);
        staMag->setWaveformID(wid);
        org->add(staMag.get());
        evt->staMags.push_back(staMag);
        Notifier::SetEnabled(false);

        // Logging
        string resultstr;
        ostringstream out;
        out.precision(2);
        out.setf(ios::fixed, ios::floatfield);
        out << "Sensor: " << it->first.first << "." << locationCode << ".";
        out << it->first.second << "." << channelCode << "; ";
        out << "Wavetype: " << std::string(input.PSclass.toString()) << "; ";
        out << "Soil class: " << std::string(input.SOILclass.toString())
            << "; ";
        out << "Magnitude: " << input.mest;
        resultstr = out.str();
        out.str("");
        SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());
        out.precision(2);
        out.setf(ios::fixed, ios::floatfield);
        out << "station lat: " << input.lat << "; station lon: " << input.lon;
        out << "; epicentral distance: " << epicdist << ";";
        resultstr = out.str();
        out.str("");
        SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());
        out.precision(2);
        out.setf(ios::scientific, ios::floatfield);
        out << "PGA(Z): " << input.ZA / 100. << "; PGV(Z): " << input.ZV / 100.;
        out << "; PGD(Z): " << input.ZD / 100.;
        resultstr = out.str();
        out.str("");
        SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());
        out << "PGA(H): " << input.HA / 100. << "; PGV(H): " << input.HV / 100.;
        out << "; PGD(H): " << input.HD / 100.;
        resultstr = out.str();
        SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());
    }

    if ( inputs.empty() ) {
        SEISCOMP_LOG(_processingInfoChannel,
                     "End logging for event: %s", event->publicID().c_str());
        return;
    }

    // Grid search
    float mag = 0.5f;
    float minL = -1.0f;
    float minMag = mag;

    while ( mag <= 9.0f ) {
        float L = 0.0f;

        for ( size_t i = 0; i < inputs.size(); ++i ) {
            const VsInput &input = inputs[i];

            // Likelihood
            vs.setmag(mag);
            L += vs.likelihood(input.ZA, input.ZV, input.ZD, input.HA, input.HV,
                               input.HD, input.PSclass, input.SOILclass, input.lat,
                               input.lon);
        }

        if ( minL < 0 || minL > L ) {
            minL = L;
            minMag = mag;
        }

        mag += 0.01f;
    }

    // calculate the median of all station magnitudes
    size_t size = inputs.size();
    double *mestarray = new double[size];

    for ( size_t i = 0; i < size; ++i ) {
        const VsInput &input = inputs[i];
        mestarray[i] = input.mest;
    }

    nth_element(mestarray, mestarray + size / 2, mestarray + size);
    stmag = mestarray[size / 2];
    delete[] mestarray;

    // TODO: Define errors
    evt->vsMagnitude = minMag;
    evt->vsStationCount = inputs.size();


    if ( _timeline.pollbuffer(evt->lat, evt->lon,evt->dthresh,evt->allThresholdStationsCount) != no_problem) {
        SEISCOMP_WARNING("Problems in the buffer polling function.");
        return;
    }


    // Use quality control functions to decide if the event is valid
    evt->isValid = false;
    double deltamag;
    double deltapick;
    if ( isEventValid(stmag, evt, evt->likelihood, deltamag, deltapick) ) {
        evt->isValid = true;
    }

    // logging
    string resultstr;
    ostringstream out;
    out.precision(2);
    out.setf(ios::fixed, ios::floatfield);
    out << "VS-mag: " << minMag << "; median single-station-mag: " << stmag;
    out << "; lat: " << evt->lat << "; lon: " << evt->lon;
    out << "; depth : " << evt->dep << " km";
    resultstr = out.str();
    out.str("");
    SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());

    out << "creation time: " << _currentTime.toString("%FT%T.%2fZ");
    out << "; origin time: " << evt->time.toString("%FT%T.%2fZ");
    Core::TimeSpan difftime = _currentTime - evt->time;
    Core::Time now = Core::Time::GMT();
    Core::TimeSpan difftime_oa = now - evt->originArrivalTime;
    Core::TimeSpan difftime_ct = now - evt->originCreationTime;
    out << "; t-diff: " << difftime.length();
    out.precision(3);
    out << "; time since origin arrival: " << difftime_oa.length();
    out << "; time since origin creation: " << difftime_ct.length();
    resultstr = out.str();
    out.str("");
    SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());

    out << "# picked stations: " << evt->pickedStationsCount; // all stations with picks
    out << "; # envelope streams: " << _timeline.StreamCount(); // all stations with envelope streams
    resultstr = out.str();
    out.str("");
    SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());

    // distance threshold for delta-pick quality criteria
    out.precision(2);
    out << "Distance threshold (dt): " << Math::Geo::deg2km(evt->dthresh)
        << " km";
    out << "; # picked stations < dt: " << evt->pickedThresholdStationsCount;
    out << "; # envelope streams < dt: " << evt->allThresholdStationsCount;
    resultstr = out.str();
    out.str("");
    SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());

    if (evt->pickedStationsCount > evt->vsStationCount) {
        out << "Stations not used for VS-mag: ";
        // find picked stations that don't contribute to the VS magnitude
        Timeline::StationList &sl = evt->pickedStations;
        for (Timeline::StationList::iterator it=sl.begin(); it!=sl.end(); ++it) {
            if ( evt->stations.find(*it) == evt->stations.end() || unused.find(*it) != unused.end()) {
                out << (*it).first << '.' << (*it).second << ' ';
            }
        }
        resultstr = out.str();
        out.str("");
        SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());
    }

    out.precision(3);
    out << "Magnitude check: " << deltamag << "; Arrivals check: " << deltapick;
    out << "; Azimuthal gap: " << evt->azGap;
    resultstr = out.str();
    out.str("");
    SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());

    out.precision(2);
    out << "likelihood: " << evt->likelihood;
    resultstr = out.str();
    out.str("");
    SEISCOMP_LOG(_processingInfoChannel, "%s", resultstr.c_str());

    SEISCOMP_LOG(_processingInfoChannel,
                 "End logging for event: %s", event->publicID().c_str());
}
Пример #4
0
/*!
 \brief Extract information from event object.

 \param event  Seiscomp3 type Event
 */
void VsMagnitude::handleEvent(Event *event) {
    _cache.feed(event);

    double dmax = 0; // dmax distance of the furthest picked station from the epicenter
    double davg = 0; // average distance of all picked stations from the epicenter
    double dsum = 0; // sum of distances
    double dthresh;

    /// get the preferred origin of the event
    OriginPtr org = _cache.get<Origin>(event->preferredOriginID());
    if ( !org ) {
        SEISCOMP_WARNING("Object %s not found in cache\nIs the cache size big enough?\n"
                         "Have you subscribed to all necessary message groups?",
                         event->preferredOriginID().c_str());
        return;
    }
    /// search for the corresponding VsEvent in the cache
    VsEventPtr vsevent;
    VsEvents::iterator it = _events.find(event->publicID());
    if ( it == _events.end() ) {
        /// if not found, create a new VsEvent
        vsevent = new VsEvent;
        if ( _expirationTimeReference == "ct" ) {
            vsevent->expirationTime = _currentTime + Core::TimeSpan(_eventExpirationTime, 0);
        } else if ( _expirationTimeReference == "ot" ) {
            vsevent->expirationTime = org->time().value() + Core::TimeSpan(_eventExpirationTime, 0);
            if ( !(_currentTime < vsevent->expirationTime) )
                return;
        }
        // set time to track how long it takes to estimate the magnitude
        // and how long it took for the origin to arrive
        vsevent->originArrivalTime = Core::Time::GMT();
        vsevent->originCreationTime = org->creationInfo().creationTime();

        // check whether event has been published already
        EventIDBuffer::iterator cev = _publishedEvents.find(event->publicID());
        if ( cev != _publishedEvents.end() ) {
            SEISCOMP_DEBUG("Event %s has already been published", event->publicID().c_str());
            return;
        }
        vsevent->update = -1;
        vsevent->maxAzGap = _maxazgap;
        /// ...and attach it to the cache (_events)
        _events[event->publicID()] = vsevent;
    }
    /// if found, use the existing one
    else
        vsevent = it->second;

    /// Populate the vsevent with data from the preferred origin of the sc3 event
    vsevent->lat = org->latitude().value();
    vsevent->lon = org->longitude().value();
    vsevent->dep = org->depth().value();
    vsevent->time = org->time().value();

    if ( _expirationTimeReference == "ot" )
        vsevent->expirationTime = org->time().value() + Core::TimeSpan(_eventExpirationTime, 0);

    /// Generate some statistics for later use in delta-pick quality measure
    Timeline::StationList pickedThresholdStations; // all picked stations at a limited distance from the epicenter
    vsevent->pickedStations.clear();
    SEISCOMP_DEBUG("Number of arrivals in origin %s: %d", org->publicID().c_str(), (int)org->arrivalCount());
    vsevent->stations.clear();
    for ( size_t i = 0; i < org->arrivalCount(); ++i ) {
        Arrival *arr = org->arrival(i);
        PickPtr pick = _cache.get<Pick>(arr->pickID());
        if ( !pick ) {
            SEISCOMP_DEBUG("cache.get<Pick>(\"%s\") failed to return pick", arr->pickID().c_str());
            continue;
        }
        Timeline::StationID id(pick->waveformID().networkCode(),
                               pick->waveformID().stationCode());

        // if the station is not yet in the pickedStations set
        if ( vsevent->pickedStations.find(id) == vsevent->pickedStations.end() ) {
            double dist = arr->distance();
            if ( dist > dmax )
                dmax = dist;
            vsevent->pickedStations.insert(id);
            dsum += dist;
        }

        // if Station already used, continue
        if ( vsevent->stations.find(id) != vsevent->stations.end() )
            continue;

        VsTimeWindow &tw = vsevent->stations[id];
        tw.setStartTime(pick->time().value() - Core::TimeSpan(_twstarttime, 0));
        tw.setEndTime(pick->time().value() + Core::TimeSpan(_twendtime, 0));
        tw.setPickTime(pick->time().value());
        // Todo: make sure that at least three seconds of data after the pick
        // are available
    }

    // count the number of arrivals with epicentral distance (in degrees)
    // less than the threshold for use in deltaPick()
    vsevent->pickedStationsCount = vsevent->pickedStations.size();
    davg = dsum / (double) vsevent->pickedStationsCount;
    // calculate threshold
    dthresh = 0.5 * (dmax + davg);
    for ( size_t i = 0; i < org->arrivalCount(); i++ ) {
        Arrival *arr = org->arrival(i);
        PickPtr pick = _cache.get<Pick>(arr->pickID());
        if ( !pick ) {
            SEISCOMP_DEBUG("cache.get<Pick>(\"%s\") failed to return pick", arr->pickID().c_str());
            continue;
        }
        Timeline::StationID id(pick->waveformID().networkCode(),
                               pick->waveformID().stationCode());
        if ( pick && arr->distance() < dthresh ) { // if the
            pickedThresholdStations.insert(id);
        }
    }

    vsevent->pickedThresholdStationsCount = pickedThresholdStations.size();

    vsevent->dthresh = dthresh;
    SEISCOMP_DEBUG("dmax; %f, davg: %f, dthresh: %f", dmax, davg, dthresh);

    // Get azimuthal gap
    vsevent->azGap = org->quality().azimuthalGap();
}