1 /*
2  * Copyright 2018 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <ctype.h>
7 #include "bs_types.h"
8 #include "bs_tracing.h"
9 #include "bs_oswrap.h"
10 #include "p2G4_pending_tx_rx_list.h"
11 #include "channel_multiatt_args.h"
12 #include "channel_if.h"
13 
14 static uint n_devices;
15 static ch_multiatt_args_t args;
16 
17 #define UNINITIALIZED 0
18 #define CONSTANT_ATT  1
19 #define FROM_FILE     2
20 
21 #define MAXLINESIZE 2048
22 
23 /* Structure to keep the status of each path file */
24 typedef struct {
25   char *filename;
26   FILE *fileptr;
27   bs_time_t last_time;
28   bs_time_t next_time;
29   double last_att;
30   double next_att;
31 } file_status_t;
32 
33 typedef struct {
34   uint ndevices;
35   double distance_exp;
36   double attenuation;
37   double atxtra;
38   char *matrix_file_name;
39   char *path_mode; //For each path, in which mode is it running
40   double *paths_att;
41   file_status_t *files_status;
42 } channel_status_t;
43 /* All the status of the channel */
44 static channel_status_t channel_status;
45 
46 /**
47  * Read a line from a file into a buffer (s), while
48  * removing duplicate spaces (unless they are quoted), comments (#...), and ":",
49  * and empty lines
50  * The string will be null terminated
51  *
52  * Return: The number of characters copied into s
53  */
att_readline(char * s,int size,FILE * stream)54 static int att_readline(char *s, int size, FILE *stream) {
55   int c = 0, i=0;
56   bool was_a_space = true;
57   bool in_a_string = false;
58 
59   while ((i == 0) && (c != EOF)) {
60     while ((i < size - 1) && ((c=getc(stream)) != EOF) && c!='\n') {
61       if (isspace(c) && (!in_a_string)) {
62         if (was_a_space) {
63           continue;
64         }
65         was_a_space = true;
66       } else {
67         was_a_space = false;
68       }
69       if ((c == ':') && (!in_a_string)) {
70         continue;
71       }
72       if (c=='"') {
73         in_a_string = !in_a_string;
74       }
75       if (c == '#') {
76         bs_skipline(stream);
77         break;
78       }
79       s[i++] =c;
80     }
81   }
82   s[i] = 0;
83 
84   if (i >= size - 1) {
85     bs_trace_warning_line("Truncated line while reading from file after %i chars\n",size-1);
86   }
87   return i;
88 }
89 
90 /*
91  * Return the length of a string.
92  * Where the strign was terminated by "\0" or "\""
93  */
att_strlen(char * input)94 static int att_strlen(char *input) {
95   uint i=1;
96   while (input[i] != 0 && input[i] != '"') {
97     i++;
98   }
99   return i;
100 }
101 
102 /**
103  * Copy a string ("<string>") into output and null terminate it
104  * note that the "" will be removed, and that the input pointer is expected to point at the first "
105  */
att_copy_string(char * input,char * output,uint * n)106 static int att_copy_string(char *input, char* output, uint *n) {
107   uint i=1;
108   while (input[i] != 0 && input[i] != '"') {
109     output[i-1] = input[i];
110     i++;
111   }
112   *n = i;
113   output[i-1] = 0;
114 
115   if (i == 1) {
116     return -1;
117   } else {
118     return 0;
119   }
120 }
121 
122 /**
123  * Initialize the reading/interpolation of attenuations
124  * from an attenuation file (for one path)
125  */
init_distance_file(file_status_t * this_f,uint index)126 static uint init_distance_file(file_status_t *this_f, uint index) {
127   //Try to read first line
128   // if succeeded set last_time, last_att to that
129   //Try to read next line
130   // if succeeded
131   //    set next_time and next_att to that
132   //    return FROM_FILE;
133   // if not succeded (only 1 line in file)
134   //    set the paths_att[index] = last_att + atxtra
135   //    return CONSTANT_ATT
136 
137   int read;
138   char line_buf[MAXLINESIZE];
139   char *filename = this_f->filename;
140   this_f->fileptr = bs_fopen(filename, "r");
141 
142   //we try to read the first line:
143   read = att_readline(line_buf, MAXLINESIZE, this_f->fileptr);
144   if (read == 0) {
145     bs_trace_error_line("file %s seems empty\n",filename);
146   }
147   read = sscanf(line_buf, "%"SCNtime" %le", &this_f->last_time , &this_f->last_att);
148   if (read < 2) {
149     bs_trace_error_line("file %s seems corrupted\n",filename);
150   }
151 
152   //we try to read the second line:
153   uint failed = 0;
154   read = att_readline(line_buf, MAXLINESIZE, this_f->fileptr);
155   if (read == 0) {
156     failed = 1;
157   } else {
158     read = sscanf(line_buf, "%"SCNtime" %le", &this_f->next_time , &this_f->next_att);
159     if (read == 0) {
160       failed = 1;
161     }
162     if (read == 1) {
163       bs_trace_error_line("file %s seems corrupted\n",filename);
164     }
165   }
166   if (failed == 0) {
167     return FROM_FILE;
168   } else {
169     channel_status.paths_att[index] = this_f->last_att + channel_status.atxtra;
170     return CONSTANT_ATT;
171   }
172 }
173 
174 /**
175  * Return the attenuation (for a path whose attenuation is being read/interpolated from file data)
176  */
att_from_file(file_status_t * this_status,bs_time_t now,uint index)177 static double att_from_file(file_status_t *this_status, bs_time_t now, uint index) {
178   //while now >= next_time:
179   //  move next_time to last_time
180   //  move next_att to last_att
181   //  try to read next line
182   //  if no next line
183   //    overwrite the mode to CONSTANT_ATT, set the paths_att[index] = last_att + atxtra
184   //    return paths_att[index]
185   //  else
186   //    set next_time and next_att to the value,
187   //
188   //if now <= last_time:
189   // return last_att + PL_status.atxtra
190   //elseif (in the middle)
191   // interpolate attenuation,
192   // return that attenuation + atxtra
193 
194   while (now >= this_status->next_time){
195     uint failed = 0;
196     int read;
197     char line_buf[MAXLINESIZE];
198 
199     this_status->last_time = this_status->next_time;
200     this_status->last_att = this_status->next_att;
201 
202     read = att_readline(line_buf, MAXLINESIZE, this_status->fileptr);
203     if (read == 0) {
204       failed = 1;
205     } else {
206       read = sscanf(line_buf, "%"SCNtime" %le", &this_status->next_time , &this_status->next_att);
207       if (read == 0) {
208         failed = 1;
209       }
210       if (read == 1) {
211         bs_trace_error_line("file %s seems corrupted\n",this_status->filename);
212       }
213     }
214     if ( failed == 1 ) { //no need to call this function again, the value wont change
215       channel_status.paths_att[index] = this_status->last_att + channel_status.atxtra;
216       channel_status.path_mode[index] = CONSTANT_ATT;
217       return channel_status.paths_att[index];
218     }
219   }
220 
221   if (now <= this_status->last_time) {
222     return this_status->last_att + channel_status.atxtra;
223   } else { //in between last_time and next_time
224     double AttInterp = (this_status->next_att - this_status->last_att)
225                        * ((double)(now - this_status->last_time)
226                        / (double)(this_status->next_time - this_status->last_time))
227                        + this_status->last_att;
228     return AttInterp + channel_status.atxtra;
229   }
230 
231   //Unreachable
232   return channel_status.atxtra;
233 }
234 
process_matrix_file_line(char * buf,uint * rx,uint * tx,double * att,char ** filename)235 static void process_matrix_file_line(char *buf, uint *rx, uint *tx, double *att, char **filename) {
236   uint read;
237   uint off;
238   uint ok;
239   uint points_to_file = 0;
240   char *endp;
241   *filename = NULL;
242   const char corruptmsg[] = "Corrupted attenuation matrix file (format txnbr rxnbr : {attenuation|\"filename\"}\n";
243 
244   *tx = strtoul(buf, &endp, 0);
245   if (endp == buf) {
246     bs_trace_error_line(corruptmsg);
247   }
248   buf = endp;
249   *rx = strtoul(buf, &endp, 0);
250   if (endp == buf) {
251     bs_trace_error_line(corruptmsg);
252   }
253   buf = endp;
254 
255   { //check if next meaningfull char is " or a number
256     off = 0;
257     while (buf[off] != 0) {
258       if (buf[off] == '"') {
259         points_to_file = 1;
260         break;
261       }
262       if ((buf[off] >= '0') && (buf[off] <= '9')) {
263         break;
264       }
265       off++;
266     } //while
267   }
268   if (points_to_file) {
269     uint length = att_strlen(&buf[off]);
270     *filename = (char*) bs_calloc(length, sizeof(char));
271     ok = att_copy_string(&buf[off], *filename, &read);
272     if (ok == -1) {
273       bs_trace_error_line(corruptmsg);
274     }
275   } else {
276     read = sscanf(&buf[off], "%le", att);
277     if (read < 1) {
278       bs_trace_error_line(corruptmsg);
279     }
280   }
281 }
282 
283 /**
284  * Return the path loss from <tx> -> <rx> in this instant (<Now>)
285  */
calculate_att(uint tx,uint rx,bs_time_t Now)286 static double calculate_att(uint tx, uint rx, bs_time_t Now) {
287   uint index = rx * channel_status.ndevices + tx;
288   if (channel_status.path_mode[index] == CONSTANT_ATT) {
289     return channel_status.paths_att[index];
290   } else if (channel_status.path_mode[index] == FROM_FILE) {
291     return att_from_file(&channel_status.files_status[index], Now, index);
292   } else {
293     bs_trace_error_line("bad error: path status not initialized or corrupted\n");
294     return 0;
295   }
296 }
297 
298 /**
299  * Initialize the channel
300  */
channel_init(int argc,char * argv[],uint n_devs)301 int channel_init(int argc, char *argv[], uint n_devs) {
302   n_devices = n_devs;
303 
304   channel_multiatt_argparse(argc, argv, &args);
305 
306   channel_status.ndevices = n_devs;
307   channel_status.attenuation = args.att;
308   channel_status.atxtra = args.attextra;
309   channel_status.matrix_file_name = args.matrix_file_name;
310 
311   channel_status.path_mode = (char*)bs_calloc(n_devs*n_devs, sizeof(char));
312   channel_status.paths_att = (double*)bs_calloc(n_devs*n_devs, sizeof(double));
313   channel_status.files_status = (file_status_t *)bs_calloc(n_devs*n_devs, sizeof(file_status_t));
314 
315   if (args.matrix_file_name != NULL) {
316     //Try to open the file
317 
318     FILE *matrix_file;
319     char line_buf[MAXLINESIZE];
320     int read = 0;
321     matrix_file = bs_fopen(args.matrix_file_name, "r");
322 
323     while (true) {
324       uint rx, tx;
325       double att;
326       char *filename;
327       uint index;
328       read = att_readline(line_buf, MAXLINESIZE, matrix_file);
329       if (read == 0) {
330         break;
331       }
332       process_matrix_file_line(line_buf, &rx, &tx, &att, &filename);
333       if ((rx >= n_devs) || (tx >= n_devs)) {
334         bs_trace_warning_line("The distances matrix file is trying to define the path from %i->%i, but only %i are set in the simulation => will be ignored\n",
335                               tx, rx, n_devs);
336         free(filename);
337         continue;
338       }
339 
340       index = rx * n_devs + tx;
341       channel_status.files_status[index].filename = filename;
342 
343       if (channel_status.path_mode[index] != UNINITIALIZED) {
344         bs_trace_warning_line("Redefinition of the path (%i->%i) attenuation\n", tx, rx);
345       }
346 
347       if (filename == NULL) {
348         channel_status.paths_att[index] = att + channel_status.atxtra;
349         channel_status.path_mode[index] = CONSTANT_ATT;
350       } else {
351         channel_status.path_mode[index] = init_distance_file(&channel_status.files_status[index], index);
352       }
353 
354     } //while (EOF matrix file)
355     fclose(matrix_file);
356   } //if there is a matrix file
357 
358   //set the default power level for all remaining paths
359   uint tx,rx;
360   for (tx = 0 ; tx < n_devs; tx++) {
361     for (rx = 0; rx < n_devs; rx++) {
362       if (tx == rx) {
363         continue;
364       }
365       uint index = rx * n_devs + tx;
366       if (channel_status.path_mode[index] == UNINITIALIZED) {
367         if (args.matrix_file_name != NULL) {
368           bs_trace_warning_line("The distance matrix file did not set the path %i->%i. It will be set to at + atxtra (%lf+%lf)\n",tx, rx, channel_status.attenuation, channel_status.atxtra);
369         }
370         channel_status.paths_att[index] = channel_status.attenuation + channel_status.atxtra;
371         channel_status.path_mode[index] = CONSTANT_ATT;
372       }
373     }
374   } //for tx
375   return 0;
376 }
377 
378 /**
379  * Recalculate the fading and path loss of the channel in this current moment (<now>)
380  * in between the N used paths and the receive path (<rxnbr>)
381  *
382  * inputs:
383  *  tx_used    : array with n_devs elements, 0: that tx is not transmitting,
384  *                                           1: that tx is transmitting,
385  *               e.g. {0,1,1,0}: devices 1 and 2 are transmitting, device 0 and 3 are not.
386  *  tx_list    : array with all transmissions status (the channel can check here the modulation type of the transmitter if necessary)
387  *               (ignored in this channel)
388  *  txnbr      : desired transmitter number (the channel will calculate the ISI only for the desired transmitter)
389  *               (ignored in this channel)
390  *  rxnbr      : device number which is receiving
391  *               (ignored in this channel)
392  *  now        : current time
393  *               (ignored in this channel)
394  *  att        : array with n_devs elements. The channel will overwrite the element i
395  *               with the average attenuation from path i to rxnbr (in dBs)
396  *               The caller allocates this array
397  *  ISI_SNR    : The channel will return here an estimate of the SNR limit due to multipath
398  *               caused ISI for the desired transmitter (in dBs)
399  *               (This channel sets this value always to 100.0)
400  *
401  * Returns < 0 on error.
402  * 0 otherwise
403  */
channel_calc(const uint * tx_used,tx_el_t * tx_list,uint txnbr,uint rxnbr,bs_time_t now,double * att,double * ISI_SNR)404 int channel_calc(const uint *tx_used, tx_el_t *tx_list, uint txnbr, uint rxnbr, bs_time_t now, double *att, double *ISI_SNR) {
405   uint tx_i;
406   for (tx_i = 0 ; tx_i < n_devices; tx_i++) {
407     if (tx_used[tx_i]) {
408       att[tx_i] = calculate_att(tx_i, rxnbr, now);
409     }
410   }
411   *ISI_SNR = 100;
412 
413   return 0;
414 }
415 
416 /**
417  * Clean up: Free the memory the channel may have allocate
418  * close any file descriptors etc.
419  * (the simulation has ended)
420  */
channel_delete()421 void channel_delete() {
422   if (channel_status.path_mode != NULL) {
423     free(channel_status.path_mode);
424   }
425   if (channel_status.paths_att != NULL) {
426     free(channel_status.paths_att);
427   }
428   if (channel_status.files_status != NULL) {
429     uint tx;
430     uint rx;
431     for (tx = 0 ; tx < channel_status.ndevices ; tx ++) {
432       for ( rx = 0; rx < channel_status.ndevices; rx++ ){
433         uint index = rx * channel_status.ndevices + tx;
434         if (channel_status.files_status[index].fileptr != NULL) {
435           fclose(channel_status.files_status[index].fileptr);
436         }
437         if (channel_status.files_status[index].filename != NULL) {
438           free(channel_status.files_status[index].filename);
439         }
440       }
441     }
442     free(channel_status.files_status);
443   }
444 }
445