Skip to content

Commit 898968e

Browse files
committed
Add a '_nanmask' channel to flag missing samples
1 parent 8ec1135 commit 898968e

File tree

1 file changed

+113
-34
lines changed

1 file changed

+113
-34
lines changed

core/src/G3SuperTimestream.cxx

Lines changed: 113 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,46 @@ inline int32_t _read_size(struct flac_helper *fh)
134134
return v;
135135
}
136136

137+
std::vector<bool> find_time_gaps(const G3VectorTime &times)
138+
{
139+
std::vector<double> dts;
140+
dts.reserve(times.size() - 1);
141+
for (size_t i = 1; i < times.size(); i++)
142+
dts.push_back(times[i].time - times[i - 1].time);
143+
144+
double dt;
145+
{
146+
int mid = dts.size() / 2;
147+
std::vector<double> sorted = dts;
148+
std::nth_element(sorted.begin(), sorted.begin() + mid, sorted.end());
149+
dt = sorted[mid];
150+
}
151+
152+
std::vector<bool> gaps;
153+
gaps.reserve(times.size());
154+
gaps.push_back(false);
155+
156+
for (size_t i = 0; i < dts.size(); i++) {
157+
int missing = (int)(std::round(dts[i] / dt - 1));
158+
for (int j = 0; j < missing; j++)
159+
gaps.push_back(true);
160+
gaps.push_back(false);
161+
}
162+
163+
return gaps;
164+
}
165+
166+
template <typename T>
167+
void fill_gaps(struct flac_helper *fh, const std::vector<bool> &gaps)
168+
{
169+
std::vector<T> src((T*)fh->dest, (T*)fh->dest + fh->count);
170+
T *dest = (T*)fh->dest;
171+
172+
int sidx = 0;
173+
for (size_t didx = 0; didx < gaps.size(); didx++)
174+
dest[didx] = (!gaps[didx] && sidx < fh->count) ? src[sidx++] : 0.0;
175+
}
176+
137177
template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
138178
{
139179
G3_CHECK_VERSION(v);
@@ -172,7 +212,10 @@ template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
172212
} else
173213
log_fatal("No support for compression algorithm times_algo=%d", (int)times_algo);
174214

175-
// XXX check times for missing samples
215+
// Create a mask of missing samples
216+
G3Time start = times[0];
217+
G3Time stop = times[times.size() - 1];
218+
std::vector<bool> time_gaps = find_time_gaps(times);
176219

177220
ar & make_nvp("names", names);
178221

@@ -186,37 +229,44 @@ template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
186229
size_t elsize = 0;
187230
int ndet = shape[0];
188231
int nsamp = shape[1];
189-
size_t size = ndet * nsamp;
232+
int nsamp_filled = time_gaps.size();
233+
size_t size = ndet * nsamp_filled;
234+
235+
// Add bad sample mask to map as an extra detector channel at the end
236+
bool found_gaps = (nsamp_filled != nsamp);
237+
if (found_gaps) {
238+
names.push_back("_nanmask");
239+
size += nsamp_filled;
240+
}
190241

191242
ar & make_nvp("data_algo", data_algo);
192243
char *buf, *cbuf;
193244

194-
// XXX handle nans and missing samples
195245
switch (type_num) {
196246
case TYPE_NUM_INT32: {
197247
std::shared_ptr<int32_t[]> data(new int32_t[size]);
198-
FromBuffer(names, nsamp, data, times[0], times[nsamp - 1]);
248+
FromBuffer(names, nsamp_filled, data, start, stop);
199249
buf = (char *)data.get();
200250
elsize = sizeof(int32_t);
201251
break;
202252
}
203253
case TYPE_NUM_FLOAT32: {
204254
std::shared_ptr<float[]> data(new float[size]);
205-
FromBuffer(names, nsamp, data, times[0], times[nsamp - 1]);
255+
FromBuffer(names, nsamp_filled, data, start, stop);
206256
buf = (char *)data.get();
207257
elsize = sizeof(int32_t);
208258
break;
209259
}
210260
case TYPE_NUM_INT64: {
211261
std::shared_ptr<int64_t[]> data(new int64_t[size]);
212-
FromBuffer(names, nsamp, data, times[0], times[nsamp - 1]);
262+
FromBuffer(names, nsamp_filled, data, start, stop);
213263
buf = (char *)data.get();
214264
elsize = sizeof(int64_t);
215265
break;
216266
}
217267
case TYPE_NUM_FLOAT64: {
218268
std::shared_ptr<double[]> data(new double[size]);
219-
FromBuffer(names, nsamp, data, times[0], times[nsamp - 1]);
269+
FromBuffer(names, nsamp_filled, data, start, stop);
220270
buf = (char *)data.get();
221271
elsize = sizeof(int64_t);
222272
break;
@@ -225,39 +275,58 @@ template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
225275
log_fatal("Invalid type num %d", (int) type_num);
226276
}
227277

228-
if (data_algo == ALGO_NONE) {
229-
ar & make_nvp("data_raw", binary_data(buf, nbytes));
230-
return;
278+
// Copy mask data to map
279+
if (found_gaps) {
280+
auto maskts = (*this)["_nanmask"];
281+
for (int i=0; i<nsamp_filled; i++)
282+
(*maskts)[i] = (double)time_gaps[i];
231283
}
232284

233-
int count;
234285
std::vector<int> offsets;
235286
std::vector<double> quanta;
236287

237-
// Read the flacblock
238-
ar & make_nvp("quanta", quanta);
239-
ar & make_nvp("offsets", offsets);
240-
ar & make_nvp("payload_bytes", count);
241-
cbuf = new char[count];
242-
ar & make_nvp("payload", binary_data(cbuf, count));
288+
if (data_algo == ALGO_NONE) {
289+
if (!found_gaps) {
290+
ar & make_nvp("data_raw", binary_data(buf, nbytes));
291+
return;
292+
}
293+
294+
for (int i=0; i<ndet; i++)
295+
offsets.push_back(elsize * nsamp * i);
296+
297+
cbuf = new char[nbytes];
298+
ar & make_nvp("data_raw", binary_data(cbuf, nbytes));
299+
} else {
300+
// Read the flacblock
301+
ar & make_nvp("quanta", quanta);
302+
ar & make_nvp("offsets", offsets);
303+
304+
int count;
305+
ar & make_nvp("payload_bytes", count);
306+
cbuf = new char[count];
307+
ar & make_nvp("payload", binary_data(cbuf, count));
308+
}
243309

244310
// Decompress or copy into a buffer.
245311
FLAC__StreamDecoderWriteCallback this_write_callback;
246312
void (*expand_func)(struct flac_helper *, int, char*) = nullptr;
247313
void (*broadcast_func)(struct flac_helper *, int) = nullptr;
314+
void (*gapfill_func)(struct flac_helper *, const std::vector<bool> &) = nullptr;
248315

249316
switch (type_num) {
250317
case TYPE_NUM_INT32:
251318
case TYPE_NUM_FLOAT32:
252319
this_write_callback = &write_callback_int<int32_t>;
253320
expand_func = expand_branch<int32_t>;
254321
broadcast_func = broadcast_val<int32_t>;
322+
gapfill_func = fill_gaps<int32_t>;
255323
break;
256324
case TYPE_NUM_INT64:
257325
case TYPE_NUM_FLOAT64:
258326
this_write_callback = &write_callback_int<int64_t>;
259327
expand_func = expand_branch<int64_t>;
260328
broadcast_func = broadcast_val<int64_t>;
329+
gapfill_func = fill_gaps<int64_t>;
261330
break;
262331
default:
263332
__builtin_unreachable(); // Handled above
@@ -272,7 +341,7 @@ template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
272341

273342
#pragma omp for
274343
for (int i=0; i<ndet; i++) {
275-
char* this_data = buf + elsize * nsamp * i;
344+
char* this_data = buf + elsize * nsamp_filled * i;
276345

277346
// Cue up this detector's data and read the algo code.
278347
helper.src = cbuf + offsets[i];
@@ -312,23 +381,33 @@ template <class A> void G3SuperTimestream::load(A &ar, unsigned v)
312381
}
313382

314383
// Now convert for precision.
315-
switch (type_num) {
316-
case TYPE_NUM_FLOAT32: {
317-
auto src = (int32_t*)this_data;
318-
auto dest = (float*)this_data;
319-
for (int j=0; i<nsamp; i++)
320-
dest[j] = (float)src[j] * quanta[i];
321-
break;
384+
if (algo != ALGO_NONE) {
385+
switch (type_num) {
386+
case TYPE_NUM_FLOAT32: {
387+
auto src = (int32_t*)this_data;
388+
auto dest = (float*)this_data;
389+
for (int j=0; i<nsamp; i++)
390+
dest[j] = (float)src[j] * quanta[i];
391+
break;
392+
}
393+
case TYPE_NUM_FLOAT64: {
394+
auto src = (int64_t*)this_data;
395+
auto dest = (double*)this_data;
396+
for (int j=0; j<nsamp; j++)
397+
dest[j] = (double)src[j] * quanta[i];
398+
break;
399+
}
400+
default:
401+
break;
402+
}
322403
}
323-
case TYPE_NUM_FLOAT64: {
324-
auto src = (int64_t*)this_data;
325-
auto dest = (double*)this_data;
326-
for (int j=0; j<nsamp; j++)
327-
dest[j] = (double)src[j] * quanta[i];
328-
break;
329-
}
330-
default:
331-
break;
404+
405+
// Fill missing samples with zeros
406+
if (found_gaps) {
407+
helper.dest = this_data;
408+
helper.start = 0;
409+
helper.count = nsamp;
410+
gapfill_func(&helper, time_gaps);
332411
}
333412

334413
}

0 commit comments

Comments
 (0)