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