RocketLogger  1.1.4
file_handling.c
Go to the documentation of this file.
1 
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <time.h>
35 
36 #include "pru.h"
37 #include "util.h"
38 
39 #include "file_handling.h"
40 
42 const char* channel_names[NUM_CHANNELS] = {"I1H", "I1L", "V1", "V2",
43  "I2H", "I2L", "V3", "V4"};
45 const char* digital_input_names[NUM_DIGITAL_INPUTS] = {"DI1", "DI2", "DI3",
46  "DI4", "DI5", "DI6"};
48 const char* valid_info_names[NUM_I_CHANNELS] = {"I1L_valid", "I2L_valid"};
49 
54 
60 void file_setup_lead_in(struct rl_file_lead_in* lead_in, struct rl_conf* conf) {
61 
62  // number channels
63  uint16_t channel_count = count_channels(conf->channels);
64  // number binary channels
65  uint16_t channel_bin_count = 0;
67  channel_bin_count = NUM_DIGITAL_INPUTS;
68  }
69  if (conf->channels[I1L_INDEX] == CHANNEL_ENABLED) {
70  i1l_valid_channel = channel_bin_count++;
71  }
72  if (conf->channels[I2L_INDEX] == CHANNEL_ENABLED) {
73  i2l_valid_channel = channel_bin_count++;
74  }
75 
76  // comment length
77  uint32_t comment_length = RL_FILE_COMMENT_ALIGNMENT_BYTES;
78 
79  // timestamps
80  struct time_stamp timestamp_realtime;
81  struct time_stamp timestamp_monotonic;
82  create_time_stamp(&timestamp_realtime, &timestamp_monotonic);
83 
84  // lead_in setup
85  lead_in->magic = RL_FILE_MAGIC;
86  lead_in->file_version = RL_FILE_VERSION;
87  lead_in->header_length =
88  sizeof(struct rl_file_lead_in) + comment_length +
89  (channel_count + channel_bin_count) * sizeof(struct rl_file_channel);
90  lead_in->data_block_size = conf->sample_rate / conf->update_rate;
91  lead_in->data_block_count = 0; // needs to be updated
92  lead_in->sample_count = 0; // needs to be updated
93  lead_in->sample_rate = conf->sample_rate;
94  get_mac_addr(lead_in->mac_address);
95  lead_in->start_time = timestamp_realtime;
96  lead_in->comment_length = comment_length;
97  lead_in->channel_bin_count = channel_bin_count;
98  lead_in->channel_count = channel_count;
99 }
100 
106 void file_setup_channels(struct rl_file_header* file_header,
107  struct rl_conf* conf) {
108  int total_channel_count = file_header->lead_in.channel_bin_count +
109  file_header->lead_in.channel_count;
110 
111  // reset channels
112  memset(file_header->channel, 0,
113  total_channel_count * sizeof(struct rl_file_channel));
114 
115  // digital channels
116  int ch = 0;
117  if (conf->digital_inputs == DIGITAL_INPUTS_ENABLED) {
118  for (int i = 0; i < NUM_DIGITAL_INPUTS; i++) {
119  file_header->channel[ch].unit = RL_UNIT_BINARY;
120  file_header->channel[ch].channel_scale = RL_SCALE_NONE;
121  file_header->channel[ch].data_size = 0;
122  file_header->channel[ch].valid_data_channel = NO_VALID_DATA;
123  strcpy(file_header->channel[ch].name, digital_input_names[i]);
124  ch++;
125  }
126  }
127 
128  // range valid channels
129  if (conf->channels[I1L_INDEX] == CHANNEL_ENABLED) {
130  file_header->channel[ch].unit = RL_UNIT_RANGE_VALID;
131  file_header->channel[ch].channel_scale = RL_SCALE_NONE;
132  file_header->channel[ch].data_size = 0;
133  file_header->channel[ch].valid_data_channel = NO_VALID_DATA;
134  strcpy(file_header->channel[ch].name, valid_info_names[0]);
135  ch++;
136  }
137  if (conf->channels[I2L_INDEX] == CHANNEL_ENABLED) {
138  file_header->channel[ch].unit = RL_UNIT_RANGE_VALID;
139  file_header->channel[ch].channel_scale = RL_SCALE_NONE;
140  file_header->channel[ch].data_size = 0;
141  file_header->channel[ch].valid_data_channel = NO_VALID_DATA;
142  strcpy(file_header->channel[ch].name, valid_info_names[1]);
143  ch++;
144  }
145 
146  // analog channels
147  for (int i = 0; i < NUM_CHANNELS; i++) {
148  if (conf->channels[i] == CHANNEL_ENABLED) {
149  // current
150  if (is_current(i)) {
151  // low
152  if (is_low_current(i)) {
153  file_header->channel[ch].channel_scale = RL_SCALE_TEN_PICO;
154  if (i == I1L_INDEX) {
155  file_header->channel[ch].valid_data_channel =
157  } else {
158  file_header->channel[ch].valid_data_channel =
160  }
161  // high
162  } else {
163  file_header->channel[ch].channel_scale = RL_SCALE_NANO;
164  file_header->channel[ch].valid_data_channel = NO_VALID_DATA;
165  }
166  file_header->channel[ch].unit = RL_UNIT_AMPERE;
167  // voltage
168  } else {
169  file_header->channel[ch].unit = RL_UNIT_VOLT;
170  file_header->channel[ch].channel_scale = RL_SCALE_TEN_NANO;
171  file_header->channel[ch].valid_data_channel = NO_VALID_DATA;
172  }
173  file_header->channel[ch].data_size = 4;
174  strcpy(file_header->channel[ch].name, channel_names[i]);
175  ch++;
176  }
177  }
178 }
179 
186 void file_setup_header(struct rl_file_header* file_header, struct rl_conf* conf,
187  char* comment) {
188 
189  // comment
190  if (comment == NULL) {
191  file_header->comment = "";
192  } else {
193  file_header->comment = comment;
194  }
195 
196  // channels
197  file_setup_channels(file_header, conf);
198 }
199 
205 void file_store_header_bin(FILE* data_file,
206  struct rl_file_header* file_header) {
207 
208  int total_channel_count = file_header->lead_in.channel_bin_count +
209  file_header->lead_in.channel_count;
210 
211  // check if alignment bytes are needed after header comment
212  int comment_length = strlen(file_header->comment) + 1;
213  int comment_align_bytes = 0;
214  if (comment_length % RL_FILE_COMMENT_ALIGNMENT_BYTES > 0) {
215  comment_align_bytes =
217  (comment_length % RL_FILE_COMMENT_ALIGNMENT_BYTES);
218  }
219 
220  file_header->lead_in.comment_length = comment_length + comment_align_bytes;
221 
222  file_header->lead_in.header_length =
223  sizeof(struct rl_file_lead_in) + file_header->lead_in.comment_length +
224  total_channel_count * sizeof(struct rl_file_channel);
225 
226  // write lead-in
227  fwrite(&(file_header->lead_in), sizeof(struct rl_file_lead_in), 1,
228  data_file);
229 
230  // write comment, add zero bytes for proper header alignment if necessary
231  fwrite(file_header->comment, comment_length, 1, data_file);
232  if (comment_align_bytes > 0) {
233  uint8_t zero_bytes[RL_FILE_COMMENT_ALIGNMENT_BYTES] = {0};
234  fwrite(zero_bytes, comment_align_bytes, 1, data_file);
235  }
236 
237  // write channel information
238  fwrite(file_header->channel, sizeof(struct rl_file_channel),
239  total_channel_count, data_file);
240  fflush(data_file);
241 }
242 
248 void file_store_header_csv(FILE* data_file,
249  struct rl_file_header* file_header) {
250  // lead-in
251  fprintf(data_file, "RocketLogger CSV File\n");
252  fprintf(data_file, "File Version,%u\n",
253  (uint32_t)file_header->lead_in.file_version);
254  fprintf(data_file, "Block Size,%u\n",
255  (uint32_t)file_header->lead_in.data_block_size);
256  fprintf(data_file, "Block Count,%-20u\n",
257  (uint32_t)file_header->lead_in.data_block_count);
258  fprintf(data_file, "Sample Count,%-20llu\n",
259  (uint64_t)file_header->lead_in.sample_count);
260  fprintf(data_file, "Sample Rate,%u\n",
261  (uint32_t)file_header->lead_in.sample_rate);
262  fprintf(data_file, "MAC Address,%02x",
263  (uint32_t)file_header->lead_in.mac_address[0]);
264 
265  for (int i = 1; i < MAC_ADDRESS_LENGTH; i++) {
266  fprintf(data_file, ":%02x",
267  (uint32_t)file_header->lead_in.mac_address[i]);
268  }
269  fprintf(data_file, "\n");
270 
271  time_t time = (time_t)file_header->lead_in.start_time.sec;
272  fprintf(data_file, "Start Time,%s", ctime(&time));
273  fprintf(data_file, "Comment,%s\n", file_header->comment);
274  fprintf(data_file, "\n");
275 
276  // channels
277  for (int i = 0; i < (file_header->lead_in.channel_count +
278  file_header->lead_in.channel_bin_count);
279  i++) {
280  fprintf(data_file, ",%s", file_header->channel[i].name);
281  switch (file_header->channel[i].channel_scale) {
282  case RL_SCALE_MILLI:
283  fprintf(data_file, " [m");
284  break;
285  case RL_SCALE_MICRO:
286  fprintf(data_file, " [u");
287  break;
288  case RL_SCALE_TEN_NANO:
289  fprintf(data_file, " [10n");
290  break;
291  case RL_SCALE_NANO:
292  fprintf(data_file, " [n");
293  break;
294  case RL_SCALE_TEN_PICO:
295  fprintf(data_file, " [10p");
296  break;
297  default:
298  break;
299  }
300  switch (file_header->channel[i].unit) {
301  case RL_UNIT_VOLT:
302  fprintf(data_file, "V]");
303  break;
304  case RL_UNIT_AMPERE:
305  fprintf(data_file, "A]");
306  break;
307  default:
308  break;
309  }
310  }
311  fprintf(data_file, "\n");
312  fflush(data_file);
313 }
314 
321 void file_update_header_bin(FILE* data_file,
322  struct rl_file_header* file_header) {
323 
324  // seek to beginning and rewrite lead_in
325  rewind(data_file);
326  fwrite(&(file_header->lead_in), sizeof(struct rl_file_lead_in), 1,
327  data_file);
328  fflush(data_file);
329  fseek(data_file, 0, SEEK_END);
330 }
331 
338 void file_update_header_csv(FILE* data_file,
339  struct rl_file_header* file_header) {
340  rewind(data_file);
341  fprintf(data_file, "RocketLogger CSV File\n");
342  fprintf(data_file, "File Version,%u\n",
343  (uint32_t)file_header->lead_in.file_version);
344  fprintf(data_file, "Block Size,%u\n",
345  (uint32_t)file_header->lead_in.data_block_size);
346  fprintf(data_file, "Block Count,%-20u\n",
347  (uint32_t)file_header->lead_in.data_block_count);
348  fprintf(data_file, "Sample Count,%-20llu\n",
349  (uint64_t)file_header->lead_in.sample_count);
350  fflush(data_file);
351  fseek(data_file, 0, SEEK_END);
352 }
353 
364 void file_handle_data(FILE* data_file, void* buffer_addr,
365  uint32_t sample_data_size, uint32_t samples_count,
366  struct time_stamp* timestamp_realtime,
367  struct time_stamp* timestamp_monotonic,
368  struct rl_conf* conf) {
369 
370  // write timestamp to file
371  if (conf->file_format == BIN) {
372  fwrite(timestamp_realtime, sizeof(struct time_stamp), 1, data_file);
373  fwrite(timestamp_monotonic, sizeof(struct time_stamp), 1, data_file);
374  } else if (conf->file_format == CSV) {
375  fprintf(data_file, "%lli.%09lli", timestamp_realtime->sec,
376  timestamp_realtime->nsec);
377  }
378 
379  // count channels
380  int num_channels = count_channels(conf->channels);
381 
382  // aggregation
383  int32_t aggregate_count = MIN_ADC_RATE / conf->sample_rate;
384  int32_t aggregate_channel_data[NUM_CHANNELS] = {0};
385  uint32_t aggregate_bin_data = 0xffffffff;
386 
387  // HANDLE BUFFER //
388  for (uint32_t i = 0; i < samples_count; i++) {
389 
390  // channel data variables
391  int32_t channel_data[NUM_CHANNELS] = {0};
392  uint32_t bin_data = 0x00000000;
393 
394  // read binary channels
395  uint8_t bin_adc1 = (*((int8_t*)(buffer_addr)));
396  uint8_t bin_adc2 = (*((int8_t*)(buffer_addr + 1)));
397 
398  buffer_addr += PRU_DIG_SIZE;
399 
400  // read and scale values (if channel selected)
401  int ch = 0;
402  for (int j = 0; j < NUM_CHANNELS; j++) {
403  if (conf->channels[j] == CHANNEL_ENABLED) {
404  int32_t adc_value;
405  if (sample_data_size == 4) {
406  adc_value =
407  *((int32_t*)(buffer_addr + sample_data_size * j));
408  } else {
409  adc_value =
410  *((int16_t*)(buffer_addr + sample_data_size * j));
411  }
412  channel_data[ch] =
413  (int32_t)((adc_value + calibration.offsets[j]) *
414  calibration.scales[j]);
415  ch++;
416  }
417  }
418  buffer_addr += NUM_CHANNELS * sample_data_size;
419 
420  // BINARY CHANNELS //
421 
422  // mask and combine digital inputs, if requestet
423  int bin_channel_pos = 0;
424  if (conf->digital_inputs == DIGITAL_INPUTS_ENABLED) {
425  bin_data = ((bin_adc1 & BINARY_MASK) >> 1) |
426  ((bin_adc2 & BINARY_MASK) << 2);
427  bin_channel_pos = NUM_DIGITAL_INPUTS;
428  }
429 
430  // mask and combine valid info
431  uint8_t valid1 = (~bin_adc1) & VALID_MASK;
432  uint8_t valid2 = (~bin_adc2) & VALID_MASK;
433 
434  if (conf->channels[I1L_INDEX] == CHANNEL_ENABLED) {
435  bin_data = bin_data | (valid1 << bin_channel_pos);
436  bin_channel_pos++;
437  }
438  if (conf->channels[I2L_INDEX] == CHANNEL_ENABLED) {
439  bin_data = bin_data | (valid2 << bin_channel_pos);
440  bin_channel_pos++;
441  }
442 
443  // handle data aggregation for low sampling rates
444  if (conf->sample_rate < MIN_ADC_RATE) {
445 
446  switch (conf->aggregation) {
447  case AGGREGATE_NONE:
448  rl_log(ERROR, "Low sampling rates not supported without "
449  "data aggregation.");
450  exit(ERROR);
451  break;
452 
453  case AGGREGATE_AVERAGE:
454  // accumulate intermediate samples only (skip writing)
455  if ((i + 1) % aggregate_count > 0) {
456  for (int i = 0; i < num_channels; i++) {
457  aggregate_channel_data[i] += channel_data[i];
458  }
459  aggregate_bin_data = aggregate_bin_data & bin_data;
460  continue;
461  }
462 
463  // calculate average for writing to file
464  for (int i = 0; i < num_channels; i++) {
465  channel_data[i] =
466  aggregate_channel_data[i] / aggregate_count;
467  aggregate_channel_data[i] = 0;
468  }
469 
470  bin_data = aggregate_bin_data;
471  aggregate_bin_data = 0xffffffff;
472  break;
473 
475  // drop intermediate samples (skip writing to file)
476  if (i % aggregate_count > 0) {
477  continue;
478  }
479  }
480  }
481 
482  // write data to file
483 
484  // write binary channels if enabled
485  if (bin_channel_pos > 0) {
486  if (conf->file_format == BIN) {
487  fwrite(&bin_data, sizeof(uint32_t), 1, data_file);
488  } else if (conf->file_format == CSV) {
489  uint32_t MASK = 0x01;
490  for (int j = 0; j < bin_channel_pos; j++) {
491  fprintf(data_file, (CSV_DELIMITER "%i"),
492  (bin_data & MASK) > 0);
493  MASK = MASK << 1;
494  }
495  }
496  }
497 
498  // write analog channels
499  if (conf->file_format == BIN) {
500  fwrite(channel_data, sizeof(int32_t), num_channels, data_file);
501  } else if (conf->file_format == CSV) {
502  for (int j = 0; j < num_channels; j++) {
503  fprintf(data_file, (CSV_DELIMITER "%d"), channel_data[j]);
504  }
505  fprintf(data_file, "\n");
506  }
507  }
508 
509  // flush processed data if data is stored
510  if (conf->file_format != NO_FILE && data_file != NULL) {
511  fflush(data_file);
512  }
513 }
int offsets[NUM_CHANNELS]
Channel offsets (in bit)
Definition: types.h:305
int is_low_current(int index)
Definition: util.c:67
#define NUM_DIGITAL_INPUTS
Number of RocketLogger digital channels.
Definition: types.h:110
double scales[NUM_CHANNELS]
Channel scalings.
Definition: types.h:307
#define BINARY_MASK
Mask for binary inputs read from PRU.
Definition: pru.h:150
void file_handle_data(FILE *data_file, void *buffer_addr, uint32_t sample_data_size, uint32_t samples_count, struct time_stamp *timestamp_realtime, struct time_stamp *timestamp_monotonic, struct rl_conf *conf)
void file_store_header_bin(FILE *data_file, struct rl_file_header *file_header)
uint16_t data_size
Datum size in bytes (for voltage and current)
Definition: rl_file.h:150
uint16_t header_length
Total size of the header in bytes.
Definition: rl_file.h:108
struct time_stamp start_time
Start time of the measurement in UNIX time, UTC.
Definition: rl_file.h:126
void file_update_header_bin(FILE *data_file, struct rl_file_header *file_header)
#define MAC_ADDRESS_LENGTH
MAC address length in bytes.
Definition: util.h:40
struct rl_file_lead_in lead_in
File header lead in (constant size)
Definition: rl_file.h:165
CSV format.
Definition: types.h:184
int64_t sec
Seconds in UNIX time (UTC)
Definition: util.h:47
int i1l_valid_channel
Global variable to determine i1l valid channel.
Definition: file_handling.c:51
int i2l_valid_channel
Global variable to determine i2l valid channel.
Definition: file_handling.c:53
void file_setup_channels(struct rl_file_header *file_header, struct rl_conf *conf)
void rl_log(rl_log_type type, const char *format,...)
Definition: log.c:39
struct rl_file_channel * channel
Channels definitions (binary and normal)
Definition: rl_file.h:171
int digital_inputs
En-/disable digital inputs.
Definition: types.h:265
uint64_t sample_count
Total sample count.
Definition: rl_file.h:117
uint16_t channel_bin_count
Binary channel count.
Definition: rl_file.h:132
void file_setup_header(struct rl_file_header *file_header, struct rl_conf *conf, char *comment)
#define RL_FILE_MAGIC
File header magic number (ascii RLD)
Definition: rl_file.h:60
void file_update_header_csv(FILE *data_file, struct rl_file_header *file_header)
uint16_t file_version
File version number.
Definition: rl_file.h:105
void file_setup_lead_in(struct rl_file_lead_in *lead_in, struct rl_conf *conf)
Definition: file_handling.c:60
Binary format.
Definition: types.h:185
#define RL_SCALE_NANO
Definition: rl_file.h:47
Aggregate by averaging data.
Definition: types.h:176
#define I2L_INDEX
Definition: types.h:234
#define VALID_MASK
Mask for valid bit read from PRU.
Definition: pru.h:148
#define RL_SCALE_TEN_PICO
Definition: rl_file.h:46
#define NUM_I_CHANNELS
Maximum number of RocketLogger current channels.
Definition: types.h:106
Aggregate using downsampling.
Definition: types.h:175
rl_unit unit
Channel unit.
Definition: rl_file.h:144
Definition: types.h:247
Range valid information.
Definition: rl_file.h:88
#define NUM_CHANNELS
Maximum number of RocketLogger channels.
Definition: types.h:104
#define CHANNEL_ENABLED
Channel sampling enabled.
Definition: types.h:223
void get_mac_addr(uint8_t mac_address[MAC_ADDRESS_LENGTH])
Definition: util.c:248
char * comment
Comment field.
Definition: rl_file.h:168
#define PRU_DIG_SIZE
Size of PRU digital information in bytes.
Definition: types.h:114
void create_time_stamp(struct time_stamp *timestamp_realtime, struct time_stamp *timestamp_monotonic)
Definition: util.c:223
#define NO_VALID_DATA
No additional range valid information available.
Definition: rl_file.h:70
int channels[NUM_CHANNELS]
Channels to sample.
Definition: types.h:261
const char * digital_input_names[NUM_DIGITAL_INPUTS]
Digital input names.
Definition: file_handling.c:45
uint16_t channel_count
Analog channel count.
Definition: rl_file.h:135
void file_store_header_csv(FILE *data_file, struct rl_file_header *file_header)
int is_current(int index)
Definition: util.c:53
uint32_t comment_length
Comment length.
Definition: rl_file.h:129
rl_file_format file_format
File format.
Definition: types.h:271
uint32_t magic
File magic constant.
Definition: rl_file.h:102
int update_rate
Data update rate.
Definition: types.h:257
uint32_t data_block_size
Size of the data blocks in the file in rows.
Definition: rl_file.h:111
char name[RL_FILE_CHANNEL_NAME_LENGTH]
Channel name/description.
Definition: rl_file.h:156
uint16_t sample_rate
Sampling rate of the measurement.
Definition: rl_file.h:120
Error.
Definition: types.h:200
int64_t nsec
Nanoseconds.
Definition: util.h:49
#define RL_FILE_VERSION
File format version of current implementation.
Definition: rl_file.h:63
Voltage (electric)
Definition: rl_file.h:85
int sample_rate
Sampling rate.
Definition: types.h:253
#define CSV_DELIMITER
CSV value delimiter character.
Definition: file_handling.h:47
int32_t channel_scale
Channel scale (in power of ten, for voltage and current)
Definition: rl_file.h:147
No file.
Definition: types.h:183
No aggregation.
Definition: types.h:174
uint16_t valid_data_channel
Link to channel valid data (for low-range current channels)
Definition: rl_file.h:153
int count_channels(int channels[NUM_CHANNELS])
Definition: util.c:80
#define MIN_ADC_RATE
Minimal ADC sampling rate.
Definition: types.h:134
const char * valid_info_names[NUM_I_CHANNELS]
Valid channel names.
Definition: file_handling.c:48
Current (electric)
Definition: rl_file.h:86
int8_t num_channels
Number of channels sampled.
Definition: rl_server.c:68
#define I1L_INDEX
Definition: types.h:230
uint32_t data_block_count
Number of data blocks stored in the file.
Definition: rl_file.h:114
#define RL_SCALE_MILLI
Definition: rl_file.h:50
#define RL_FILE_COMMENT_ALIGNMENT_BYTES
Comment alignment in bytes.
Definition: rl_file.h:76
rl_aggregation aggregation
Aggregation mode (for sampling rates below lowest native one)
Definition: types.h:255
Binary signal.
Definition: rl_file.h:87
#define RL_SCALE_TEN_NANO
Definition: rl_file.h:48
uint8_t mac_address[MAC_ADDRESS_LENGTH]
Instrument ID (mac address)
Definition: rl_file.h:123
#define DIGITAL_INPUTS_ENABLED
Digital input sampling ensabled.
Definition: types.h:242
#define RL_SCALE_MICRO
Definition: rl_file.h:49
const char * channel_names[NUM_CHANNELS]
Channel names.
Definition: file_handling.c:42
struct rl_calibration calibration
Calibration data.
Definition: types.h:335
#define RL_SCALE_NONE
Definition: rl_file.h:51