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