1 /*--------------------------------------------------------------------------*/
4 // This file is part of ruwai.
6 // If you use ruwai_parser in any program or publication, please inform and
7 // acknowledge its author Stefan Mertl (stefan@mertl-research.at).
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 /*--------------------------------------------------------------------------*/
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
;
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;
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
;
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);
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();
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
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;
173 //TODO: Handle the GPS timebase.
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;
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
;
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;
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));
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());
258 syslog(LOG_WARNING
, "[WARNING][add_timestamp_sample] Couldn't set the system time. ret_val = %d; %s", ret_val
, strerror(errno
));
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)
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
);
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();
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
;
369 syslog(LOG_ERR
, "[ERROR][pack_samplebuffer] No timestamp available. Writing data to miniseed without timestamp is not yet supported.");
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
)
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.
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
);
412 last_data_of_file
= true;
415 // Fill the MSRecord with the sample buffer data up to the current
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);
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
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
;
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
);
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
);
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.");
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
;
521 msr
->dataquality
= 'D';
522 msr
->sampletype
= 'i';
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()));
534 Recorder::set_msr_timeflags(MSRecord
* msr
, std::vector
<timestamp_t
>& timestamps
)
536 bool utc_available
= true;
537 bool gps_fix_ok
= true;
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
)
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;
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.
572 msr
->fsdh
->dq_flags
|= (1 << 7);
575 // Set the timing quality.
576 msr
->Blkt1001
->timing_qual
= gps_fix
;
577 msr
->Blkt1001
->framecnt
= 7;
582 Recorder::msr_fits_to_end(MSTrace
* mst
, MSRecord
* msr
)
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
)
605 Recorder::write_trace_to_file(MSTrace
* mst
, bool flush
, MSRecord
* msr_template
)
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.");
616 char time_string
[27];
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");
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
);
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));
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
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.
669 // length = 3600s: start of the file every hour.
670 // length = 300s: start of a file every 5 minutes of an hour (13:00,
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.
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
;
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.");