Write the error message to log.
[ruwai.git] / software / c++ / ruwai / src / recorder.cpp
blob12955d505189cbec6d4140e73fc59b5f7dc08c63
1 /*--------------------------------------------------------------------------*/
2 // LICENSE
3 //
4 // This file is part of ruwai.
5 //
6 // If you use ruwai_parser in any program or publication, please inform and
7 // acknowledge its author Stefan Mertl (stefan@mertl-research.at).
8 //
9 // ruwai is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 /*--------------------------------------------------------------------------*/
23 #include "recorder.h"
24 #include <bitset>
27 Recorder::Recorder(std::string serial_number, unsigned int n_channels, unsigned int sps, std::string sd_mnt, unsigned int file_length)
29 this->serial_number = serial_number;
30 this->sps = sps;
31 this->n_channels = n_channels;
32 this->write_limit = sps;
33 this->sd_mnt_point = sd_mnt;
34 tow_sub_ms_scale = std::pow(2, -32);
37 // Create one vector for each channel.
38 for (unsigned int i = 0; i < n_channels; i++)
40 sample_buffer.push_back(std::vector<std::int32_t>());
41 timestamp_pos.push_back(std::vector<int>());
42 timestamps.push_back(std::vector<timestamp_t>());
44 MSTrace* mst_template = mst_init(NULL);
45 strcpy(mst_template->network, "XX");
46 strcpy(mst_template->station, this->serial_number.c_str());
47 strcpy(mst_template->location, "00");
48 mst_template->dataquality = 'D';
49 mst_template->sampletype = 'i';
50 mst_template->samprate = this->sps;
51 mst_template->samplecnt = 0;
52 mst_template->numsamples = 0;
53 strcpy(mst_template->channel, (str(boost::format("%02d") % (i + 1)).c_str()));
55 // Initialize the MSRecord template and save it in the prvtptr.
56 MSRecord *msr_template = msr_init(NULL); // The MiniSeed Record used for adding sample data to the trace group.
57 init_msr(msr_template, i + 1); // Create the blockettes and set header fields.
58 mst_template->prvtptr = msr_template;
60 traces.push_back(mst_template);
63 // Initialize the miniseed trace group.
64 //mstg = mst_initgroup(NULL);
66 // Create the output directory string.
67 data_dir = sd_mnt_point + "/" + "mseed";
69 // Initialize the recorder state.
70 this->file_length = file_length;
72 state_gps_fix_init = false;
73 //state_gps_fix = FIX_NO;
74 //state_gps_fix_ok = false;
75 sec_slts_cnt = 0;
76 last_timestamp.time = 0;
77 last_timestamp.sec_slts = 0;
78 last_timestamp.utc_available = false;
79 last_timestamp.utc_timebase = false;
80 last_timestamp.gps_fix_ok = false;
81 last_timestamp.gps_fix = FIX_NO;
85 bool
86 Recorder::check_output_dir_structure(void)
88 // Check for the miniseed output folder. Create it if it doesn't exist.
89 if (!boost::filesystem::exists(data_dir))
91 boost::filesystem::create_directory(data_dir);
96 std::string recorder_dir = data_dir + "/" + this->serial_number;
97 if (!boost::filesystem::exists(recorder_dir))
99 boost::filesystem::create_directory(recorder_dir);
102 return true;
105 void
106 Recorder::add_sample(rw_smp_24bit_t paket)
108 mut_sample_buffer.lock();
109 for (unsigned int i = 0; i < sample_buffer.size(); i++)
111 sample_buffer[i].push_back(paket.samples[i]);
114 mut_sample_buffer.unlock();
118 void
119 Recorder::add_timestamp_sample(rw_smp_24bit_tsp_t paket)
121 syslog(LOG_DEBUG, "[add_timestamp_sample] Adding a timestamp sample.");
122 bool set_system_time = false;
124 // Add the samples to the sample buffer.
125 mut_sample_buffer.lock();
126 mut_timestamps.lock();
127 for (unsigned int i = 0; i < sample_buffer.size(); i++)
129 sample_buffer[i].push_back(paket.samples[i]);
130 timestamp_pos[i].push_back(sample_buffer[i].size() - 1);
133 mut_timestamps.unlock();
134 mut_sample_buffer.unlock();
136 // Convert the GPS timestamp to hptime used by libmseed.
137 // GPS time starts on 1980-01-06 UTC.
138 // Unix epoch time starts on 1970-01-01 UTC.
139 // The difference between GPS time and Unix epoch time is 315964800
140 // seconds.
141 timestamp_t cur_timestamp;
142 cur_timestamp.utc_available = (bool)paket.flags.utc;
143 cur_timestamp.utc_timebase = (bool)paket.flags.timebase;
144 cur_timestamp.gps_fix_ok = (bool)paket.flags.gps_fix_ok;
145 cur_timestamp.gps_fix = (gpsfix_t)paket.flags.gps_fix;
146 cur_timestamp.sec_slts = paket.sec_slts;
148 //std::cout << "week: " << paket.week << "," << "tow_ms: " << paket.tow_ms << "," << "tow_sub_ms: " << paket.tow_sub_ms << ", sec_slts: " << paket.sec_slts << std::endl;
149 syslog(LOG_INFO, "[add_timestamp_sample] paket time info: week: %d, tow_ms: %d, tow_sub_ms: %d, sec_slts: %d", paket.week, paket.tow_ms, paket.tow_sub_ms, paket.sec_slts);
150 double gps_timestamp = (double)paket.week * (double)604800 + (double)paket.tow_ms / (double)1000 + (double)paket.tow_sub_ms/(double)1000 * tow_sub_ms_scale;
151 double tow_sub_ms = (double)paket.tow_sub_ms/(double)1000 * tow_sub_ms_scale;
153 // Add the seconds since the last timestamp.
154 gps_timestamp += paket.sec_slts;
156 syslog(LOG_DEBUG, "[add_timestamp_sample] scaled tow_sub_ms: %f", tow_sub_ms);
157 syslog(LOG_DEBUG, "[add_timestamp_sample] gps_timestamp: %f", gps_timestamp);
158 //std::cout << "tow_sub_ms: " << str(boost::format("%f") % tow_sub_ms) << std::endl;
159 //std::cout << "gps_timestamp: " << str(boost::format("%f") % gps_timestamp) << std::endl;
162 // Correct the timestamp with the ADC group delay.
163 // TODO: Handle the group delay for high-resolution mode (39/f_data).
164 gps_timestamp -= 38/(float)sps;
166 double epoch_timestamp;
167 if(cur_timestamp.utc_timebase)
169 epoch_timestamp = gps_timestamp + 315964800;
171 else
173 //TODO: Handle the GPS timebase.
174 epoch_timestamp = 0;
178 if (epoch_timestamp >= 0)
180 cur_timestamp.time = MS_EPOCH2HPTIME(epoch_timestamp);
181 mut_timestamps.lock();
182 for (unsigned int k = 0; k < timestamps.size(); k++)
184 timestamps[k].push_back(cur_timestamp);
185 syslog(LOG_DEBUG, "[add_timestamp_sample] sample_buffer[%d].size(): %d", k, (int)sample_buffer[k].size());
186 syslog(LOG_DEBUG, "[add_timestamp_sample] Added timestamp_pos: %d", (int)timestamp_pos[k].back());
187 //std::cout << "sample_buffer[" << k << "].size(): " << sample_buffer[k].size() << "\n";
189 mut_timestamps.unlock();
192 char time_string[27];
193 syslog(LOG_INFO, "[add_timestamp_sample] timestamp: %s", ms_hptime2isotimestr(cur_timestamp.time, time_string, true));
194 syslog(LOG_INFO, "[add_timestamp_sample] hp_time: %lld", (long long int)cur_timestamp.time);
195 syslog(LOG_INFO, "[add_timestamp_sample] gps_fix_ok: %d", cur_timestamp.gps_fix_ok);
196 syslog(LOG_INFO, "[add_timestamp_sample] gps_fix: %d", cur_timestamp.gps_fix);
197 syslog(LOG_INFO, "[add_timestamp_sample] utc_available: %d", cur_timestamp.utc_available);
198 syslog(LOG_INFO, "[add_timestamp_sample] utc_timebase: %d", cur_timestamp.utc_timebase);
200 // Check the time fix state and log any changes.
201 if (!state_gps_fix_init)
203 syslog(LOG_NOTICE, "[add_timestamp_sample] GPS_FIX state initialized: timestamp = %s, gps_fix = %d, gps_fix_ok = %d, utc_available = %d", ms_hptime2isotimestr(cur_timestamp.time, time_string, true), cur_timestamp.gps_fix, cur_timestamp.gps_fix_ok, cur_timestamp.utc_available);
204 state_gps_fix_init = true;
206 else
208 if (cur_timestamp.gps_fix != last_timestamp.gps_fix)
210 syslog(LOG_NOTICE, "[add_timestamp_sample] GPS_FIX changed: timestamp = %s, gps_fix = %d, gps_fix_ok = %d, utc_available = %d", ms_hptime2isotimestr(cur_timestamp.time, time_string, true), cur_timestamp.gps_fix, cur_timestamp.gps_fix_ok, cur_timestamp.utc_available);
213 if (cur_timestamp.gps_fix_ok != last_timestamp.gps_fix_ok)
215 syslog(LOG_NOTICE, "[add_timestamp_sample] GPS_FIX_OK changed: timestamp = %s, gps_fix = %d, gps_fix_ok = %d, utc_available = %d", ms_hptime2isotimestr(cur_timestamp.time, time_string, true), cur_timestamp.gps_fix, cur_timestamp.gps_fix_ok, cur_timestamp.utc_available);
218 if (cur_timestamp.utc_available != last_timestamp.utc_available)
220 syslog(LOG_NOTICE, "[add_timestamp_sample] UTC_AVAILABLE changed: timestamp = %s, gps_fix = %d, gps_fix_ok = %d, utc_available = %d", ms_hptime2isotimestr(cur_timestamp.time, time_string, true), cur_timestamp.gps_fix, cur_timestamp.gps_fix_ok, cur_timestamp.utc_available);
223 hptime_t ts_diff = cur_timestamp.time - last_timestamp.time;
224 if (ts_diff < 0)
226 syslog(LOG_WARNING, "[WARNING][add_timestamp_sample] Negative timestamp difference of %lld us detected. current: %s, last: %s", (long long int)ts_diff, ms_hptime2isotimestr(cur_timestamp.time, time_string, true), ms_hptime2isotimestr(last_timestamp.time, time_string, true));
227 set_system_time = true;
229 else if (ts_diff > 1000000)
231 syslog(LOG_WARNING, "[WARNING][add_timestamp_sample] Positive timestamp difference of %lld us detected. current: %s, last: %s", (long long int)ts_diff, ms_hptime2isotimestr(cur_timestamp.time, time_string, true), ms_hptime2isotimestr(last_timestamp.time, time_string, true));
232 set_system_time = true;
234 else if (ts_diff == 0)
236 syslog(LOG_WARNING, "[WARNING][add_timestamp_sample] Equal timestamp difference of %lld us detected. current: %s, last: %s", (long long int)ts_diff, ms_hptime2isotimestr(cur_timestamp.time, time_string, true), ms_hptime2isotimestr(last_timestamp.time, time_string, true));
237 set_system_time = true;
239 else if (ts_diff > 0 && ts_diff < 1000000)
241 syslog(LOG_WARNING, "[WARNING][add_timestamp_sample] Timestamp difference between 0 and 1 of %lld us detected. current: %s, last: %s", (long long int)ts_diff, ms_hptime2isotimestr(cur_timestamp.time, time_string, true), ms_hptime2isotimestr(last_timestamp.time, time_string, true));
242 set_system_time = true;
244 else
246 syslog(LOG_INFO, "[add_timestamp_sample] Correct timestamp difference of %lld us detected. current: %s, last: %s", (long long int)ts_diff, ms_hptime2isotimestr(cur_timestamp.time, time_string, true), ms_hptime2isotimestr(last_timestamp.time, time_string, true));
250 if (set_system_time)
252 int ret_val;
253 std::string cmd = "set_system_time " + str(boost::format("%lld") % (long long int)(MS_HPTIME2EPOCH(cur_timestamp.time)));
254 syslog(LOG_NOTICE, "[add_timestamp_sample] Setting the system time using the command: %s.", cmd.c_str());
255 ret_val = system(cmd.c_str());
256 if (ret_val != 0)
258 syslog(LOG_WARNING, "[WARNING][add_timestamp_sample] Couldn't set the system time. ret_val = %d; %s", ret_val, strerror(errno));
260 else
262 syslog(LOG_NOTICE, "[add_timestamp_sample] Successfully set the system time.");
266 // Log the seconds since the last timestamp.
267 if (cur_timestamp.sec_slts > 0)
269 sec_slts_cnt++;
270 if (sec_slts_cnt >= 5)
272 syslog(LOG_NOTICE, "[add_timestamp_sample] Running without valid timestamp: timestamp = %s, sec_slts = %d", time_string, cur_timestamp.sec_slts);
273 sec_slts_cnt = 0;
276 else
278 sec_slts_cnt = 0;
281 last_timestamp = cur_timestamp;
283 // Pack the samples to a miniseed file.
284 std::thread t_pack_samplebuffer(&Recorder::pack_samplebuffer, this);
285 t_pack_samplebuffer.detach();
289 void
290 Recorder::pack_samplebuffer(void)
292 syslog(LOG_DEBUG, "[pack_samplebuffer] Packing the samplebuffer to miniseed.");
294 auto start = std::chrono::high_resolution_clock::now();
296 hptime_t last_starttime;
298 // TODO: Copy the sample_buffer, timestamp_pos and timestamps to current
299 // working variables. Limit the access to the vectors to a single position
300 // protected by the mutex locks.
301 // Copy the data until the last valid timestamp. Keep the last timestamp in
302 // the original vectors, remove the rest.
304 std::vector <std::vector <std::int32_t> > pack_buffer;
305 std::vector <std::vector <int> > pack_timestamp_pos;
306 std::vector <std::vector <timestamp_t> > pack_timestamps;
308 syslog(LOG_DEBUG, "[pack_samplebuffer] Copying from the recording vectors....");
309 mut_timestamps.lock();
310 for (unsigned int cur_channel = 0; cur_channel < n_channels; cur_channel++)
312 syslog(LOG_DEBUG, "[pack_samplebuffer] Processing channel %d.", cur_channel);
313 // Copy the timestamps and timestamp_positions until the last
314 // timestamp. Keep the last timestamp in the recording vector.
315 syslog(LOG_DEBUG, "[pack_samplebuffer] timestamps size: %d", (int)timestamps[cur_channel].size());
316 syslog(LOG_DEBUG, "[pack_samplebuffer] timestamp_pos size: %d", (int)timestamp_pos[cur_channel].size());
317 syslog(LOG_DEBUG, "[pack_samplebuffer] timestamps_pos end: %d", (int)timestamp_pos[cur_channel].back());
318 std::vector <timestamp_t> cur_ts(timestamps[cur_channel].begin(), timestamps[cur_channel].end());
319 pack_timestamps.push_back(cur_ts);
320 std::vector <std::int32_t> cur_ts_pos(timestamp_pos[cur_channel].begin(), timestamp_pos[cur_channel].end());
321 pack_timestamp_pos.push_back(cur_ts_pos);
322 timestamps[cur_channel].erase(timestamps[cur_channel].begin(), timestamps[cur_channel].end());
323 timestamp_pos[cur_channel].erase(timestamp_pos[cur_channel].begin(), timestamp_pos[cur_channel].end());
324 syslog(LOG_DEBUG, "[pack_samplebuffer] pack_timestamps size: %d", (int)pack_timestamps[cur_channel].size());
325 syslog(LOG_DEBUG, "[pack_samplebuffer] pack_timestamp_pos size: %d", (int)pack_timestamp_pos[cur_channel].size());
326 syslog(LOG_DEBUG, "[pack_samplebuffer] timestamps size after erase: %d", (int)timestamps[cur_channel].size());
327 syslog(LOG_DEBUG, "[pack_samplebuffer] timestamp_pos size after erase: %d", (int)timestamp_pos[cur_channel].size());
329 // Copy the samples up to the last timestamp_pos from the sample
330 // buffer. Remove the copied samples from the sample buffer.
331 int last_ts_pos = pack_timestamp_pos[cur_channel].back();
332 mut_sample_buffer.lock();
333 syslog(LOG_DEBUG, "[pack_samplebuffer] last_ts_pos: %d", last_ts_pos);
334 syslog(LOG_INFO, "[pack_samplebuffer] sample_buffer[%d].size(): %d", cur_channel, (int)sample_buffer[cur_channel].size());
335 std::vector <std::int32_t> cur_buffer(sample_buffer[cur_channel].begin(), sample_buffer[cur_channel].begin() + last_ts_pos); // The last element is not included by the copy constructor.
336 pack_buffer.push_back(cur_buffer);
337 sample_buffer[cur_channel].erase(sample_buffer[cur_channel].begin(), sample_buffer[cur_channel].begin() + last_ts_pos);
338 syslog(LOG_INFO, "[pack_samplebuffer] pack_buffer.size(): %d", (int)pack_buffer.size());
339 syslog(LOG_INFO, "[pack_samplebuffer] pack_buffer[%d].size(): %d", cur_channel, (int)pack_buffer[cur_channel].size());
340 syslog(LOG_INFO, "[pack_samplebuffer] sample_buffer size after erase: %d", (int)sample_buffer[cur_channel].size());
341 mut_sample_buffer.unlock();
343 mut_timestamps.unlock();
344 syslog(LOG_DEBUG, "[pack_samplebuffer] .....finished copying from the recording vectors.");
347 // Process the copied data of each channel.
348 for (unsigned int cur_channel = 0; cur_channel < n_channels; cur_channel++)
350 syslog(LOG_INFO, "[pack_samplebuffer] Packing channel %d.", cur_channel + 1);
351 hptime_t span_start; // The start time of the timespan to write to file.
352 hptime_t span_end; // The end time of the timespan to write to file.
353 bool flush_trace = false; // If true, write the whole data in trace to file.
354 bool last_data_of_file = false; // True if the end of the file time-span was reached.
355 char time_string[27]; // Used to convert hptime to string.
357 std::vector <std::int32_t> rem_buffer = std::vector <std::int32_t>(); // The remaining data if the file time-span was reached.
359 MSTrace *cur_mst = traces[cur_channel];
360 MSRecord *msr = (MSRecord*) cur_mst->prvtptr;
362 // Compute the time of the first sample in the sample_buffer_vector.
363 if (pack_timestamps[cur_channel].size() >= 1)
365 msr->starttime = pack_timestamps[cur_channel].front().time - (pack_timestamp_pos[cur_channel].front() * 1/(float)sps) * (double)HPTMODULUS;
367 else
369 syslog(LOG_ERR, "[ERROR][pack_samplebuffer] No timestamp available. Writing data to miniseed without timestamp is not yet supported.");
370 return;
373 // Add the timestamp quality information to the record.
374 set_msr_timeflags(msr, pack_timestamps[cur_channel]);
376 // Compute the time-span limits of the desired file length.
377 hptime_t file_length_hp = MS_EPOCH2HPTIME((double)file_length);
378 span_start = msr->starttime - (msr->starttime % file_length_hp);
379 span_end = span_start + file_length_hp;
381 // If the the data spans the split-limit of a file defined above. Write the
382 // remaining samples to the miniseed file and keep the rest for the next
383 // call of the method. Make sure to set the flag in mst_pack to write the
384 // data even though that no complete record is filled.
385 if (pack_timestamps[cur_channel].back().time >= span_end)
387 int pos_limit;
388 pos_limit = pack_timestamp_pos[cur_channel].back();
389 syslog(LOG_DEBUG, "[pack_samplebuffer] old pos_limit: %d", pos_limit);
390 pos_limit = pos_limit - (pack_timestamps[cur_channel].back().time - span_end) / (double)HPTMODULUS * sps;
391 syslog(LOG_DEBUG, "[pack_samplebuffer] new computed pos_limit: %d", pos_limit);
393 // Check if the timegap between the span_end and the current
394 // timestamp is within the valid data limit. If not, there was a
395 // jump in the timestamp (e.g. new GPS fix) and the data before the
396 // current timestamp can't be correctly assigned to a new file.
397 if (pos_limit < 0)
399 pos_limit = timestamp_pos[cur_channel].back();
400 syslog(LOG_DEBUG, "[pack_samplebuffer] fixed zero pos_limit: %d", pos_limit);
403 // Copy the remaining samples. Add them to the trace group, after
404 // the trace was packed.
405 rem_buffer = pack_buffer[cur_channel];
406 rem_buffer.erase(rem_buffer.begin(), rem_buffer.begin() + pos_limit); // The last element is not included by the copy constructor.
408 // resize the pack buffer
409 pack_buffer[cur_channel].resize(pos_limit);
411 flush_trace = true;
412 last_data_of_file = true;
415 // Fill the MSRecord with the sample buffer data up to the current
416 // timestamp.
417 msr->datasamples = &pack_buffer[cur_channel][0];
418 msr->numsamples = pack_buffer[cur_channel].size();
419 msr->samplecnt = pack_buffer[cur_channel].size();
421 // Add the MSRecord to the trace of the current channel.
422 syslog(LOG_INFO, "[pack_samplebuffer] Adding MSR to the trace.");
423 syslog(LOG_INFO, "[pack_samplebuffer] cur_mst->starttime isoformat: %s", ms_hptime2isotimestr(cur_mst->endtime, time_string, true));
424 syslog(LOG_INFO, "[pack_samplebuffer] cur_mst->endtime isoformat: %s", ms_hptime2isotimestr(cur_mst->endtime, time_string, true));
425 syslog(LOG_INFO, "[pack_samplebuffer] msr->starttime isoformat: %s", ms_hptime2isotimestr(msr->starttime, time_string, true));
427 // Check, if the MSRecord can be added to the end of the trace.
428 if(msr_fits_to_end(cur_mst, msr))
430 // Add the record to the end of the trace.
431 syslog(LOG_INFO, "[pack_samplebuffer] Sample gap is ok. Adding the record to the end of the trace.");
432 mst_addmsr(cur_mst, msr, 1);
434 else
436 // The record doesn't fit at the end of the trace.
437 // Flush the trace to file and add the record to an empty trace.
438 syslog(LOG_WARNING, "[pack_samplebuffer] Sample gap is too large or negative. Flush the trace to file and add the record to an empty trace.");
439 write_trace_to_file(cur_mst, true, msr);
441 // Reset the datasamples values in the MSRecord.
442 cur_mst->datasamples = NULL;
443 cur_mst->numsamples = 0;
444 cur_mst->samplecnt = 0;
446 // Set the new start time of the trace and reset the sequence
447 // number.
448 cur_mst->starttime = msr->starttime;
449 msr->sequence_number = 1;
451 mst_addmsr(cur_mst, msr, 1);
452 syslog(LOG_NOTICE, "[pack_samplebuffer] mst->starttime: %s", ms_hptime2isotimestr(cur_mst->starttime, time_string, true));
453 syslog(LOG_NOTICE, "[pack_samplebuffer] mst->endtime: %s", ms_hptime2isotimestr(cur_mst->endtime, time_string, true));
456 // Write the trace to file.
457 write_trace_to_file(cur_mst, flush_trace, msr);
459 if (last_data_of_file && rem_buffer.size() > 0)
461 syslog(LOG_NOTICE, "[pack_samplebuffer] Writing the last data to file.");
462 syslog(LOG_NOTICE, "[pack_samplebuffer] msr->starttime: %s", ms_hptime2isotimestr(msr->starttime, time_string, true));
463 syslog(LOG_NOTICE, "[pack_samplebuffer] mst->starttime: %s", ms_hptime2isotimestr(cur_mst->starttime, time_string, true));
464 syslog(LOG_NOTICE, "[pack_samplebuffer] mst->endtime: %s", ms_hptime2isotimestr(cur_mst->endtime, time_string, true));
465 msr->starttime = cur_mst->endtime + (1/(float)sps * (double)HPTMODULUS);
466 syslog(LOG_NOTICE, "[pack_samplebuffer] new msr->starttime: %s", ms_hptime2isotimestr(msr->starttime, time_string, true));
467 syslog(LOG_NOTICE, "[pack_samplebuffer] rem_buffer.size(): %d", (int)rem_buffer.size());
468 msr->datasamples = &rem_buffer[0];
469 msr->numsamples = rem_buffer.size();
470 msr->samplecnt = rem_buffer.size();
471 msr->sampletype = 'i';
472 // TODO: Check for valid gap at end of the trace.
473 mst_addmsr(cur_mst, msr, 1);
474 syslog(LOG_NOTICE, "[pack_samplebuffer] new mst->starttime: %s", ms_hptime2isotimestr(cur_mst->starttime, time_string, true));
475 syslog(LOG_NOTICE, "[pack_samplebuffer] new mst->endtime: %s", ms_hptime2isotimestr(cur_mst->endtime, time_string, true));
478 // Reset the datasamples values in the MSRecord.
479 msr->datasamples = NULL;
480 msr->numsamples = 0;
481 msr->samplecnt = 0;
483 // Store the last used mst starttime for later use in log message.
484 last_starttime = cur_mst->starttime;
486 auto end = std::chrono::high_resolution_clock::now();
487 auto diff = end - start;
488 long int pack_ms = (long int)std::chrono::duration_cast<std::chrono::milliseconds>(diff).count();
489 syslog(LOG_INFO, "[pack_samplebuffer] Packed samples in %ld milliseconds.", pack_ms);
490 if (pack_ms >= 800)
492 char last_starttime_string[27];
493 ms_hptime2isotimestr(last_starttime, last_starttime_string, true);
494 syslog(LOG_WARNING, "[pack_samplebuffer] Packed samples in %ld milliseconds. TOO LONG. Last mst start time: %s.", pack_ms, last_starttime_string);
500 void
501 Recorder::init_msr (MSRecord* msr, unsigned int channel_number)
503 // Create the fixed header structure.
504 msr->fsdh = (struct fsdh_s *) calloc(1, sizeof(struct fsdh_s)); // Allocate momory for the fsdh. Taken from libmseed pack.c line 530.
505 if ( msr->fsdh == NULL )
507 syslog(LOG_ERR, "[ERROR][init_msr] Can't allocate memory for the Miniseed record fixed header section.");
508 return;
511 // Create the 1001 blockette used for tracking the gps_fix values.
512 struct blkt_1001_s blkt_1001;
513 memset(&blkt_1001, 0, sizeof(struct blkt_1001_s));
514 msr_addblockette(msr, (char *) &blkt_1001, sizeof(struct blkt_1001_s), 1001, 0);
516 strcpy(msr->network, "XX");
517 strcpy(msr->station, this->serial_number.c_str());
518 strcpy(msr->location, "00");
519 msr->samprate = this->sps;
520 msr->byteorder = 0;
521 msr->dataquality = 'D';
522 msr->sampletype = 'i';
523 //msr->reclen = 512;
524 //msr->encoding = 11;
525 //msr->byteorder = 0;
527 // Adjust the channel and in the MSRecord.
528 strcpy(msr->channel, (str(boost::format("%03d") % channel_number).c_str()));
533 void
534 Recorder::set_msr_timeflags(MSRecord* msr, std::vector <timestamp_t>& timestamps)
536 bool utc_available = true;
537 bool gps_fix_ok = true;
538 uint8_t gps_fix = 6;
539 std::vector <timestamp_t>::iterator ts_it;
541 for(ts_it = timestamps.begin(); ts_it != timestamps.end(); ts_it++)
543 if (!ts_it->gps_fix_ok)
545 gps_fix_ok = false;
548 // Get the lowest GPS_FIX value. This will be used in the
549 // msr blockette 1001.
550 if (ts_it->gps_fix < gps_fix)
552 gps_fix = ts_it->gps_fix;
555 if (!ts_it->utc_available)
557 utc_available = false;
561 // Set the timelock flag.
562 msr->fsdh->io_flags = 0;
563 if(gps_fix_ok)
565 msr->fsdh->io_flags |= (1 << 5);
567 //std::cout << "io_flags: " << (int)msr->fsdh->io_flags << " - " << std::bitset<8>(msr->fsdh->io_flags).to_string() << std::endl;
569 // Use the "time tag questionable" flag to record the utc_available flag.
570 if (utc_available)
572 msr->fsdh->dq_flags |= (1 << 7);
575 // Set the timing quality.
576 msr->Blkt1001->timing_qual = gps_fix;
577 msr->Blkt1001->framecnt = 7;
581 bool
582 Recorder::msr_fits_to_end(MSTrace* mst, MSRecord* msr)
584 hptime_t postgap;
585 hptime_t hp_delta;
586 hptime_t gap_limit;
587 hp_delta = (hptime_t)((double)HPTMODULUS / (float)sps);
588 gap_limit = 1.5 * hp_delta;
589 postgap = msr->starttime - mst->endtime;
590 syslog(LOG_INFO, "[msr_fits_to_end] postgap: %lld", (long long int)postgap);
591 syslog(LOG_INFO, "[msr_fits_to_end] limit: %lld", (long long int)gap_limit);
593 if (postgap > 0 && postgap <= gap_limit)
595 return true;
597 else
599 return false;
604 void
605 Recorder::write_trace_to_file(MSTrace* mst, bool flush, MSRecord* msr_template)
607 if (mst == NULL)
608 return;
610 if (mst->numsamples == 0)
612 syslog(LOG_WARNING, "[WARNING][write_trace_to_file] No data samples in the trace. Leaving write_trace_to_file.");
613 return;
616 char time_string[27];
617 hptime_t span_start;
618 std::string cur_filename;
619 BTime file_starttime;
621 hptime_t file_length_hp = MS_EPOCH2HPTIME((double)file_length);
622 span_start = mst->starttime - (mst->starttime % file_length_hp);
623 ms_hptime2btime(span_start, &file_starttime);
624 cur_filename = get_filename(file_starttime, mst->channel);
626 FILE *out_fid = NULL;
627 int64_t packed_samples = 0;
628 int64_t packed_records = 0;
630 out_fid = fopen(cur_filename.c_str(), "ab");
632 if (out_fid != NULL)
634 syslog(LOG_INFO, "[write_trace_to_file] mst->starttime: %s", ms_hptime2isotimestr(mst->starttime, time_string, true));
635 syslog(LOG_INFO, "[write_trace_to_file] mst->endtime: %s", ms_hptime2isotimestr(mst->endtime, time_string, true));
636 syslog(LOG_INFO, "[write_trace_to_file] Packing msr to file %s.", cur_filename.c_str());
637 packed_records = mst_pack(mst, &record_handler, out_fid, 512, 11, 1, &packed_samples, (int)flush, 0, msr_template);
638 if (packed_records >= 0)
640 syslog(LOG_INFO, "[write_trace_to_file] Packed %ld samples.", (long int)packed_samples);
642 else
644 syslog(LOG_ERR, "[ERROR][write_trace_to_file] Error when packing the records using mst_pack.");
646 syslog(LOG_INFO, "[write_trace_to_file] mst->starttime: %s", ms_hptime2isotimestr(mst->starttime, time_string, true));
647 syslog(LOG_INFO, "[write_trace_to_file] mst->endtime: %s", ms_hptime2isotimestr(mst->endtime, time_string, true));
648 fclose(out_fid);
650 else
652 syslog(LOG_ERR, "[ERROR][write_trace_to_file] Error opening the file %s.", cur_filename.c_str());
654 // TODO: If there is a permanent error opening the file, the mst
655 // will grow and fill up the memory. Add some error handling
656 // discarding data after some time to prevent a blocking due to
657 // large memory use.
662 std::string
663 Recorder::get_filename(BTime start_time, char* channel)
665 // Create the output filename. The length of the miniseed files should be
666 // defined in seconds. The start of the file should be a multiple of the
667 // file length after a full hour.
668 // e.g.
669 // length = 3600s: start of the file every hour.
670 // length = 300s: start of a file every 5 minutes of an hour (13:00,
671 // 13:05, ...)
672 std::string filename;
673 std::string year_str = str(boost::format("%04d") % start_time.year);
674 std::string doy_str = str(boost::format("%03d") % start_time.day);
675 std::string hour_str = str(boost::format("%02d") % (int)start_time.hour);
676 std::string min_str = str(boost::format("%02d") % (int)start_time.min);
677 std::string sec_str = str(boost::format("%02d") % (int)start_time.sec);
679 filename = year_str + "_" + doy_str + "_" + hour_str + min_str + sec_str + "_" + serial_number + "_" + channel + ".msd";
681 // If needed create/update the ouput directory structure.
682 // YEAR
683 // ---DOY
684 // ------SERIAL_NUMBER
685 std::string dir = data_dir + "/" + year_str;
686 if (!boost::filesystem::exists(dir))
688 boost::filesystem::create_directory(dir);
691 dir = dir + "/" + doy_str;
692 if (!boost::filesystem::exists(dir))
694 boost::filesystem::create_directory(dir);
697 dir = dir + "/" + serial_number;
698 if (!boost::filesystem::exists(dir))
700 boost::filesystem::create_directory(dir);
703 filename = dir + "/" + filename;
705 return filename;
709 void
710 record_handler (char *record, int reclen, void *ptr)
712 if ( fwrite(record, reclen, 1, (FILE*)ptr) != 1 )
714 syslog(LOG_DEBUG, "[record_handler] Cannot write to output file.");