/********************************************************
* ██████╗ ██████╗████████╗██╗
* ██╔════╝ ██╔════╝╚══██╔══╝██║
* ██║ ███╗██║ ██║ ██║
* ██║ ██║██║ ██║ ██║
* ╚██████╔╝╚██████╗ ██║ ███████╗
* ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
* Geophysical Computational Tools & Library (GCTL)
*
* Copyright (c) 2022 Yi Zhang (yizhang-geo@zju.edu.cn)
*
* GCTL is distributed under a dual licensing scheme. You can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 2
* of the License, or (at your option) any later version. You should have
* received a copy of the GNU Lesser General Public License along with this
* program. If not, see .
*
* If the terms and conditions of the LGPL v.2. would prevent you from using
* the GCTL, please consider the option to obtain a commercial license for a
* fee. These licenses are offered by the GCTL's original author. As a rule,
* licenses are provided "as-is", unlimited in time for a one time fee. Please
* send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget
* to include some description of your company and the realm of its activities.
* Also add information on how to contact you by electronic and paper mail.
******************************************************/
#include "xcorrelation.h"
std::string gctl::seismic::seed_info::get_sac_filename(int id)
{
return sac_t0[id].rdseed_time_str() + "." + nwk_name + "." + stn_name + ".00." + CHANNEL_NAME[sac_chl[id]] + ".D.SAC";
}
gctl::seismic::Xcorrelation::Xcorrelation(){}
gctl::seismic::Xcorrelation::~Xcorrelation(){}
void gctl::seismic::Xcorrelation::create_stations(std::string info_list, SIG_CHANNEL working_chl)
{
std::ifstream infile;
gctl::open_infile(infile, info_list, ".md");
std::string lin_s, dum_s, wrk_dir;
std::getline(infile, lin_s);
gctl::parse_string_to_value(lin_s, ' ', false, dum_s, dum_s, wrk_dir);
seed_info sd;
int tmp_len;
double tmp_freq;
UTC_TIME tmp_t;
std::string t0_str, te_str, chl_str;
while (std::getline(infile, lin_s))
{
if (lin_s == "") continue;
sd.sac_t0.clear();
sd.sac_te.clear();
sd.sac_freq.clear();
sd.sac_len.clear();
sd.sac_chl.clear();
gctl::parse_string_to_value(lin_s, ' ', false, dum_s, dum_s, sd.dir_file);
sd.dir_file = wrk_dir + "/" + sd.dir_file;
std::getline(infile, lin_s);
gctl::parse_string_to_value(lin_s, ' ', false, dum_s, dum_s,
sd.stn_name, sd.nwk_name, sd.lat, sd.lon, sd.elev,
dum_s, dum_s, dum_s, dum_s, dum_s,
t0_str, te_str);
sd.stn_t0.set_rdseed_time(t0_str);
sd.stn_te.set_rdseed_time(te_str);
while (std::getline(infile, lin_s))
{
if (lin_s == "") break;
gctl::parse_string_to_value(lin_s, ' ', false,
dum_s, dum_s, dum_s, dum_s,
chl_str, dum_s, dum_s, dum_s,
t0_str, te_str, tmp_freq, tmp_len);
tmp_t.set_rdseed_time(t0_str);
sd.sac_t0.push_back(tmp_t);
tmp_t.set_rdseed_time(te_str);
sd.sac_te.push_back(tmp_t);
sd.sac_freq.push_back(tmp_freq);
sd.sac_len.push_back(tmp_len);
if (chl_str == "SHN") sd.sac_chl.push_back(SHN);
else if (chl_str == "SHE") sd.sac_chl.push_back(SHE);
else if (chl_str == "SHZ") sd.sac_chl.push_back(SHZ);
else if (chl_str == "BHN") sd.sac_chl.push_back(BHN);
else if (chl_str == "BHE") sd.sac_chl.push_back(BHE);
else if (chl_str == "BHZ") sd.sac_chl.push_back(BHZ);
else sd.sac_chl.push_back(UNKNOWN);
}
seeds_.push_back(sd);
}
infile.close();
station tmp_st;
fragment_info tmp_frag;
for (size_t i = 0; i < seeds_.size(); i++)
{
tmp_st.lon = seeds_[i].lon;
tmp_st.lat = seeds_[i].lat;
tmp_st.elev = seeds_[i].elev;
tmp_st.name = seeds_[i].stn_name;
tmp_st.network = seeds_[i].nwk_name;
tmp_st.frags.clear();
for (size_t j = 0; j < seeds_[i].sac_freq.size(); j++)
{
tmp_frag.seed_id = i;
tmp_frag.t0 = seeds_[i].sac_t0[j];
tmp_frag.te = seeds_[i].sac_te[j];
tmp_frag.len = seeds_[i].sac_len[j];
tmp_frag.freq= seeds_[i].sac_freq[j];
tmp_frag.chl = seeds_[i].sac_chl[j];
if (working_chl == SBHN && (tmp_frag.chl == SHN || tmp_frag.chl == BHN)) tmp_st.frags.push_back(tmp_frag);
else if (working_chl == SBHE && (tmp_frag.chl == SHE || tmp_frag.chl == BHE)) tmp_st.frags.push_back(tmp_frag);
else if (working_chl == SBHZ && (tmp_frag.chl == SHZ || tmp_frag.chl == BHZ)) tmp_st.frags.push_back(tmp_frag);
else if (tmp_frag.chl == working_chl) tmp_st.frags.push_back(tmp_frag);
}
if (stations_.empty()) stations_.push_back(tmp_st);
else
{
bool new_stn = true;
for (size_t s = 0; s < stations_.size(); s++)
{
if (tmp_st.name == stations_[s].name && tmp_st.network == stations_[s].network)
{
for (size_t j = 0; j < tmp_st.frags.size(); j++)
{
stations_[s].frags.push_back(tmp_st.frags[j]);
}
new_stn = false;
break;
}
}
if (new_stn) stations_.push_back(tmp_st);
}
}
// Sort fragments according to their start times
for (size_t i = 0; i < stations_.size(); i++)
{
std::sort(stations_[i].frags.begin(), stations_[i].frags.end(),
[](const fragment_info &a, const fragment_info &b)->bool{return (a.t0 < b.t0);});
}
return;
}
void gctl::seismic::Xcorrelation::save_stations(std::string stn_file)
{
std::ofstream ofile;
open_outfile(ofile, stn_file, ".md");
ofile << "##### seed list\n";
ofile << "seed number: " << seeds_.size() << "\n\n";
for (size_t i = 0; i < seeds_.size(); i++)
{
ofile << i+1 << ". " << seeds_[i].dir_file << "\n";
}
ofile << std::endl;
int l;
ofile << "##### station list\n";
ofile << "station number: " << stations_.size() << "\n\n";
for (size_t i = 0; i < stations_.size(); i++)
{
ofile << i+1 << " " << stations_[i].network << " " << stations_[i].name << " \n";
ofile << stations_[i].lon << " " << stations_[i].lat << " " << stations_[i].elev << " \n";
ofile << "fragment number: " << stations_[i].frags.size() << "\n\n";
l = 0;
for (size_t j = 0; j < stations_[i].frags.size(); j++)
{
ofile << l+1 << ". " << stations_[i].frags[j].seed_id + 1 << " "
<< CHANNEL_NAME[stations_[i].frags[j].chl] << " "
<< stations_[i].frags[j].t0.time_str() << " "
<< stations_[i].frags[j].te.time_str() << " "
<< stations_[i].frags[j].freq << " "
<< stations_[i].frags[j].len << "\n";
l++;
}
ofile << std::endl;
}
ofile.close();
return;
}
void gctl::seismic::Xcorrelation::create_station_sac(std::string working_dir, std::string out_dir,
sig_unit_process_ptr pre_merge_func, sig_unit_process_ptr after_merge_func)
{
int seed_id;
std::string sac_name, sys_cmd;
SAC tmp_sac, stn_sac;
std::vector sigs;
size_t i, j, s;
//#pragma omp parallel for private (i, j, s, seed_id, sac_name, sys_cmd, tmp_sac, stn_sac, sigs) schedule(guided)
for (i = 0; i < stations_.size(); i++)
{
seed_id = -1;
sigs.clear();
sigs.reserve(stations_[i].frags.size());
for (j = 0; j < stations_[i].frags.size(); j++)
{
sac_name = working_dir + "/" + stations_[i].frags[j].get_sac_filename(stations_[i].name, stations_[i].network);
if (!access(sac_name.c_str(), R_OK))
{
tmp_sac.read(sac_name);
}
else
{
// delete all sac files opened by the last recorded seed file
if (seed_id != -1)
{
for (s = 0; s < seeds_[seed_id].sac_t0.size(); s++)
{
sys_cmd = "rm " + working_dir + "/" + seeds_[seed_id].get_sac_filename(s);
system(sys_cmd.c_str());
}
}
// Call rdseed to read seed file and output SAC files into the working directory
seed_id = stations_[i].frags[j].seed_id;
sys_cmd = "rdseed -f " + seeds_[seed_id].dir_file + " -dq " + working_dir;
system(sys_cmd.c_str());
tmp_sac.read(sac_name);
}
if (pre_merge_func(tmp_sac.signal)) continue;
if (vecvalid(tmp_sac.signal.val)) sigs.push_back(tmp_sac.signal);
}
stn_sac.head = tmp_sac.head;
stn_sac.signal.merge_sig_units(sigs);
if (after_merge_func(stn_sac.signal))
{
throw std::runtime_error("[GCTL_SEIMIC] gctl::seismic::Xcorrelation::create_station_sac(...) stopped by user function.");
}
stn_sac.sync();
sac_name = out_dir + "/MERGED." + stations_[i].network + "." + stations_[i].name + "." + stn_sac.signal.t0.rdseed_time_str() + ".SAC";
stn_sacs_[stations_[i].name] = sac_name;
stn_sac.save(sac_name);
// delete all sac files opened by the last recorded seed file
if (seed_id != -1)
{
for (s = 0; s < seeds_[seed_id].sac_t0.size(); s++)
{
sys_cmd = "rm " + working_dir + "/" + seeds_[seed_id].get_sac_filename(s);
system(sys_cmd.c_str());
}
}
}
std::ofstream info_out;
open_outfile(info_out, out_dir + "/station_sac_list.txt");
info_out << stn_sacs_.size() << std::endl;
std::map::iterator iter;
for (iter = stn_sacs_.begin(); iter != stn_sacs_.end(); ++iter)
{
info_out << iter->first << " " << iter->second << std::endl;
}
info_out.close();
return;
}
void gctl::seismic::Xcorrelation::Xcorrelation_two_stations(std::string stn_sac_list, std::string stn1_name,
std::string stn2_name, SIG_UNIT &out_xc, time_unit roundup, double frag_sec, double over_lap, double sigma,
sig_unit_process_ptr sig1_func, sig_unit_process_ptr sig2_func)
{
std::ifstream list_in;
open_infile(list_in, stn_sac_list);
int stn_num;
std::vector stn_names, stn_files;
list_in >> stn_num;
stn_names.resize(stn_num);
stn_files.resize(stn_num);
for (size_t i = 0; i < stn_num; i++)
{
list_in >> stn_names[i] >> stn_files[i];
}
list_in.close();
int f = 0;
std::string stn1_sac, stn2_sac;
for (size_t i = 0; i < stn_num; i++)
{
if (stn1_name == stn_names[i]) {stn1_sac = stn_files[i]; f++;}
if (stn2_name == stn_names[i]) {stn2_sac = stn_files[i]; f++;}
if (f > 1) break;
}
if (f < 2)
{
throw std::runtime_error("Inquired station(s) not found. From gctl::seismic::Xcorrelation::Xcorrelation_two_stations(...)");
}
SAC sac1, sac2;
SIG_UNIT sig1, sig2, tmp_xc;
UTC_TIME stn1_t0, stn2_t0, cut_t0; // Start time of the cutting section
std::vector sections_1, sections_2, stored_xcs;
// read station sacs
sac1.read(stn1_sac);
sac2.read(stn2_sac);
stn1_t0 = sac1.signal.t0;
stn2_t0 = sac2.signal.t0;
cut_t0 = stn1_t0 sigma*day_std1 || sig_std2 > sigma*day_std2)
{
cut_t00.add_duration(frag_sec*over_lap); continue;
}
// Skiped by user
if (sig1_func(sig1)) {cut_t00.add_duration(frag_sec*over_lap); continue;}
if (sig2_func(sig2)) {cut_t00.add_duration(frag_sec*over_lap); continue;}
// Stored them to sections
if (vecvalid(sig1.val) && vecvalid(sig2.val))
{
sections_1.push_back(sig1);
sections_2.push_back(sig2);
}
// move cut time
cut_t00.add_duration(frag_sec*over_lap);
}
// move cut time
cut_t0.add_duration(24*3600);
}
for (size_t i = 0; i < sections_1.size(); i++)
{
std::clog << "\r" << i+1 << "/" << sections_1.size() << "\n";
tmp_xc.linear_cross_correlation(sections_1[i], sections_2[i]);
//tmp_xc.linear_cross_correlation_freq(sections_1[i], sections_2[i], 20);
stored_xcs.push_back(tmp_xc);
}
out_xc.stack_sig_units(stored_xcs);
return;
}
void gctl::seismic::Xcorrelation::Xcorrelation_all_stations(std::string working_dir,
std::string out_dir, time_unit roundup, double frag_sec)
{
UTC_TIME stn1_t0, stn2_t0, cut_t0, cut_te; // Start time of the cutting section
std::vector sections_1, sections_2, stored_xcs;
SIG_UNIT tmp_xc, station_xc;
// Loop over pairs between any two stations
int seed1_id, seed2_id;
int sac1_id, sac2_id;
int sac1_num, sac2_num;
bool new_sac1, new_sac2, end_of_station;
SAC sac1, sac2;
SIG_UNIT sac1_sig, sac2_sig;
std::string sac_name, sys_cmd;
sys_cmd = "rm " + working_dir + "/*.SAC";
system(sys_cmd.c_str());
for (size_t s = 0; s < stations_.size()-1; s++)
{
//for (size_t n = s+1; n < stations_.size(); n++)
for (size_t n = s+1; n < 2; n++)
{
//std::clog << "Calculating Xcorrleation between stations " << stations_[s].name << " and " << stations_[n].name << "...\n";
seed1_id = seed2_id = -1;
new_sac1 = new_sac2 = true;
end_of_station = false;
sac1_num = stations_[s].frags.size();
sac2_num = stations_[n].frags.size();
stored_xcs.clear();
stored_xcs.reserve(1000*(sac1_num>sac2_num?sac1_num:sac2_num));
// Continue if no frags are found
if (sac1_num == 0 || sac2_num == 0) continue;
// Find out the start time of the two comparing stations
stn1_t0 = stations_[s].frags[0].t0;
stn2_t0 = stations_[n].frags[0].t0;
cut_t0 = stn1_t0 sac1_num-1) end_of_station = true;
}
if (sac2.signal.te < cut_te)
{
new_sac2 = true; sac2_id++;
if (sac2_id > sac2_num-1) end_of_station = true;
}
}
station_xc.stack_sig_units(stored_xcs);
station_xc.save(out_dir + "/xc_" + stations_[s].name + "_" + stations_[n].name + "_" + stations_[s].network);
// delete all sac files opened by the last recorded seed file
if (seed1_id != -1)
{
for (size_t i = 0; i < seeds_[seed1_id].sac_t0.size(); i++)
{
sys_cmd = "rm " + working_dir + "/" + seeds_[seed1_id].get_sac_filename(i);
system(sys_cmd.c_str());
}
}
// delete all sac files opened by the last recorded seed file
if (seed2_id != -1)
{
for (size_t i = 0; i < seeds_[seed2_id].sac_t0.size(); i++)
{
sys_cmd = "rm " + working_dir + "/" + seeds_[seed2_id].get_sac_filename(i);
system(sys_cmd.c_str());
}
}
}
}
return;
}