root/trunk/libffado/tests/test-timestampedbuffer.cpp

Revision 445, 18.8 kB (checked in by pieterpalmers, 16 years ago)

* name change from FreeBoB to FFADO
* replaced tabs by 4 spaces
* got rid of end-of-line spaces
* made all license and copyrights conform

library becomes LGPL, apps become GPL
explicitly state LGPL v2.1 and GPL v2 (don't like v3 draft)

copyrights are 2005-2007 Daniel & Pieter
except for the MotU stuff (C) Jonathan, Pieter

Line 
1 /*
2  * Copyright (C) 2005-2007 by by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * FFADO is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * FFADO is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with FFADO; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <argp.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <endian.h>
33
34 #include <signal.h>
35 #include "src/debugmodule/debugmodule.h"
36
37 #include <netinet/in.h>
38
39 #include "src/libstreaming/cycletimer.h"
40
41 #include "src/libutil/TimestampedBuffer.h"
42
43 #include <pthread.h>
44
45 using namespace Util;
46
47 class TimestampedBufferTestClient
48     : public TimestampedBufferClient {
49 public:
50     bool processReadBlock(char *data, unsigned int nevents, unsigned int offset) {return true;};
51     bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset) {return true;};
52
53     void setVerboseLevel(int l) {setDebugLevel(l);};
54 private:
55     DECLARE_DEBUG_MODULE;
56 };
57
58 IMPL_DEBUG_MODULE( TimestampedBufferTestClient, TimestampedBufferTestClient, DEBUG_LEVEL_VERBOSE );
59
60 DECLARE_GLOBAL_DEBUG_MODULE;
61
62 int run;
63 // Program documentation.
64 static char doc[] = "FFADO -- Timestamped buffer test\n\n";
65
66 // A description of the arguments we accept.
67 static char args_doc[] = "";
68
69
70 struct arguments
71 {
72     short verbose;
73     uint64_t wrap_at;
74     uint64_t frames_per_packet;
75     uint64_t events_per_frame;
76     float rate;
77     uint64_t total_cycles;
78     uint64_t buffersize;
79     uint64_t start_at_cycle;
80 };
81
82 // The options we understand.
83 static struct argp_option options[] = {
84     {"verbose",     'v',    "n",    0,  "Verbose level" },
85     {"wrap",        'w',    "n",    0,  "Wrap at (ticks) (3072000)" },
86     {"fpp",        'f',    "n",    0,  "Frames per packet (8)" },
87     {"epf",        'e',    "n",    0,  "Events per frame (10)" },
88     {"rate",        'r',    "n",    0,  "Rate (ticks/frame) (512.0)" },
89     {"cycles",        'c',    "n",    0,  "Total cycles to run (2000)" },
90     {"buffersize",        'b',    "n",    0,  "Buffer size (in frames) (1024)" },
91     {"startcycle",        's',    "n",    0,  "Start at cycle (0)" },
92     { 0 }
93 };
94
95 //-------------------------------------------------------------
96
97 // Parse a single option.
98 static error_t
99 parse_opt( int key, char* arg, struct argp_state* state )
100 {
101     // Get the input argument from `argp_parse', which we
102     // know is a pointer to our arguments structure.
103     struct arguments* arguments = ( struct arguments* ) state->input;
104     char* tail;
105
106     switch (key) {
107         case 'v':
108             if (arg) {
109                 arguments->verbose = strtoll( arg, &tail, 0 );
110                 if ( errno ) {
111                     fprintf( stderr, "Could not parse 'verbose' argument\n" );
112                     return ARGP_ERR_UNKNOWN;
113                 }
114             } else {
115                 if ( errno ) {
116                     fprintf( stderr, "Could not parse 'verbose' argument\n" );
117                     return ARGP_ERR_UNKNOWN;
118                 }
119             }
120             break;
121         case 'w':
122             if (arg) {
123                 arguments->wrap_at = strtoll( arg, &tail, 0 );
124                 if ( errno ) {
125                     fprintf( stderr, "Could not parse 'wrap' argument\n" );
126                     return ARGP_ERR_UNKNOWN;
127                 }
128             } else {
129                 if ( errno ) {
130                     fprintf( stderr, "Could not parse 'wrap' argument\n" );
131                     return ARGP_ERR_UNKNOWN;
132                 }
133             }
134             break;
135         case 'f':
136             if (arg) {
137                 arguments->frames_per_packet = strtoll( arg, &tail, 0 );
138                 if ( errno ) {
139                     fprintf( stderr, "Could not parse 'fpp' argument\n" );
140                     return ARGP_ERR_UNKNOWN;
141                 }
142             } else {
143                 if ( errno ) {
144                     fprintf( stderr, "Could not parse 'fpp' argument\n" );
145                     return ARGP_ERR_UNKNOWN;
146                 }
147             }
148             break;
149         case 'e':
150             if (arg) {
151                 arguments->events_per_frame = strtoll( arg, &tail, 0 );
152                 if ( errno ) {
153                     fprintf( stderr, "Could not parse 'epf' argument\n" );
154                     return ARGP_ERR_UNKNOWN;
155                 }
156             } else {
157                 if ( errno ) {
158                     fprintf( stderr, "Could not parse 'epf' argument\n" );
159                     return ARGP_ERR_UNKNOWN;
160                 }
161             }
162             break;
163         case 'c':
164             if (arg) {
165                 arguments->total_cycles = strtoll( arg, &tail, 0 );
166                 if ( errno ) {
167                     fprintf( stderr, "Could not parse 'cycles' argument\n" );
168                     return ARGP_ERR_UNKNOWN;
169                 }
170             } else {
171                 if ( errno ) {
172                     fprintf( stderr, "Could not parse 'cycles' argument\n" );
173                     return ARGP_ERR_UNKNOWN;
174                 }
175             }
176             break;
177         case 's':
178             if (arg) {
179                 arguments->start_at_cycle = strtoll( arg, &tail, 0 );
180                 if ( errno ) {
181                     fprintf( stderr, "Could not parse 'startcycle' argument\n" );
182                     return ARGP_ERR_UNKNOWN;
183                 }
184             } else {
185                 if ( errno ) {
186                     fprintf( stderr, "Could not parse 'startcycle' argument\n" );
187                     return ARGP_ERR_UNKNOWN;
188                 }
189             }
190             break;
191         case 'b':
192             if (arg) {
193                 arguments->buffersize = strtoll( arg, &tail, 0 );
194                 if ( errno ) {
195                     fprintf( stderr, "Could not parse 'buffersize' argument\n" );
196                     return ARGP_ERR_UNKNOWN;
197                 }
198             } else {
199                 if ( errno ) {
200                     fprintf( stderr, "Could not parse 'buffersize' argument\n" );
201                     return ARGP_ERR_UNKNOWN;
202                 }
203             }
204             break;
205         case 'r':
206             if (arg) {
207                 arguments->rate = strtof( arg, &tail );
208                 if ( errno ) {
209                     fprintf( stderr, "Could not parse 'rate' argument\n" );
210                     return ARGP_ERR_UNKNOWN;
211                 }
212             } else {
213                 if ( errno ) {
214                     fprintf( stderr, "Could not parse 'rate' argument\n" );
215                     return ARGP_ERR_UNKNOWN;
216                 }
217             }
218             break;
219        default:
220             return ARGP_ERR_UNKNOWN;
221     }
222     return 0;
223 }
224
225 // Our argp parser.
226 static struct argp argp = { options, parse_opt, args_doc, doc };
227
228
229 static void sighandler (int sig)
230 {
231         run = 0;
232 }
233
234 int main(int argc, char *argv[])
235 {
236
237     TimestampedBuffer *t=NULL;
238     TimestampedBufferTestClient *c=NULL;
239
240     struct arguments arguments;
241
242     // Default values.
243     arguments.verbose        = 0;
244     arguments.wrap_at = 3072000LLU; // 1000 cycles
245     arguments.frames_per_packet = 8;
246     arguments.events_per_frame = 10;
247     arguments.rate = 512.0;
248     arguments.total_cycles = 2000;
249     arguments.buffersize = 1024;
250     arguments.start_at_cycle = 0;
251
252     // Parse our arguments; every option seen by `parse_opt' will
253     // be reflected in `arguments'.
254     if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) {
255         fprintf( stderr, "Could not parse command line\n" );
256         exit(1);
257     }
258
259     setDebugLevel(arguments.verbose);
260
261     run=1;
262
263     signal (SIGINT, sighandler);
264     signal (SIGPIPE, sighandler);
265
266     c=new TimestampedBufferTestClient();
267
268     if(!c) {
269         debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBufferTestClient\n");
270         exit(1);
271     }
272     c->setVerboseLevel(arguments.verbose);
273
274     t=new TimestampedBuffer(c);
275
276     if(!t) {
277         debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBuffer\n");
278         delete c;
279         exit(1);
280     }
281     t->setVerboseLevel(arguments.verbose);
282
283     t->init();
284
285     // Setup the buffer
286     t->setBufferSize(arguments.buffersize);
287     t->setEventSize(sizeof(int));
288     t->setEventsPerFrame(arguments.events_per_frame);
289
290     t->setUpdatePeriod(arguments.frames_per_packet);
291     t->setNominalRate(arguments.rate);
292
293     t->setWrapValue(arguments.wrap_at);
294
295     t->setTickOffset(10000);
296
297     t->prepare();
298
299     usleep(1000);
300
301     debugOutput(DEBUG_LEVEL_NORMAL, "Start setBufferHeadTimestamp test...\n");
302     {
303         bool pass=true;
304         uint64_t time=arguments.start_at_cycle*3072;
305         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
306
307         // initialize the timestamp
308         uint64_t timestamp=time;
309         if (timestamp >= arguments.wrap_at) {
310             // here we need a modulo because start_at_cycle can be large
311             timestamp %= arguments.wrap_at;
312         }
313
314         // account for the fact that there is offset,
315         // and that setBufferHeadTimestamp doesn't take offset
316         // into account
317         uint64_t timestamp2=timestamp+t->getTickOffset();
318         if (timestamp2>=arguments.wrap_at) {
319             timestamp2-=arguments.wrap_at;
320         }
321
322         t->setBufferHeadTimestamp(timestamp2);
323
324         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
325         if (timestamp >= arguments.wrap_at) {
326             timestamp -= arguments.wrap_at;
327         }
328
329         // write some packets
330         for (unsigned int i=0;i<20;i++) {
331             t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
332             timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
333             if (timestamp >= arguments.wrap_at) {
334                 timestamp -= arguments.wrap_at;
335             }
336         }
337
338         for(unsigned int cycle=arguments.start_at_cycle;
339             cycle < arguments.start_at_cycle+arguments.total_cycles;
340             cycle++) {
341                 uint64_t ts_head, fc_head;
342
343                 t->setBufferHeadTimestamp(timestamp);
344                 t->getBufferHeadTimestamp(&ts_head, &fc_head);
345
346                 if (timestamp != ts_head) {
347                     debugError(" cycle %4u error: %011llu != %011llu\n",
348                         timestamp, ts_head);
349                         pass=false;
350                 }
351
352                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
353                 if (timestamp >= arguments.wrap_at) {
354                     timestamp -= arguments.wrap_at;
355                 }
356
357             // simulate the cycle timer clock in ticks
358             time += 3072;
359             if (time >= arguments.wrap_at) {
360                 time -= arguments.wrap_at;
361             }
362
363             // allow for the messagebuffer thread to catch up
364             usleep(200);
365
366             if(!run) break;
367         }
368
369         if(!pass) {
370             debugError("Test failed, exiting...\n");
371
372             delete t;
373             delete c;
374
375             return -1;
376
377         }
378     }
379
380
381
382     debugOutput(DEBUG_LEVEL_NORMAL, "Start read/write test...\n");
383     {
384         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
385         int dummyframe_out[arguments.events_per_frame*arguments.frames_per_packet];
386
387         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
388             dummyframe_in[i]=i;
389         }
390
391         uint64_t time=arguments.start_at_cycle*3072;
392
393         // initialize the timestamp
394         uint64_t timestamp=time;
395         if (timestamp >= arguments.wrap_at) {
396             // here we need a modulo because start_at_cycle can be large
397             timestamp %= arguments.wrap_at;
398         }
399         t->setBufferTailTimestamp(timestamp);
400
401         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
402         if (timestamp >= arguments.wrap_at) {
403             timestamp -= arguments.wrap_at;
404         }
405
406         for(unsigned int cycle=arguments.start_at_cycle;
407             cycle < arguments.start_at_cycle+arguments.total_cycles;
408             cycle++) {
409
410             // simulate the rate adaptation
411             int64_t diff=(time%arguments.wrap_at)-timestamp;
412
413             if (diff>(int64_t)arguments.wrap_at/2) {
414                 diff -= arguments.wrap_at;
415             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
416                 diff += arguments.wrap_at;
417             }
418
419             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
420
421             if(diff>0) {
422                 uint64_t ts_head, fc_head;
423                 uint64_t ts_tail, fc_tail;
424
425                 // write one packet
426                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
427
428                 // read the buffer head timestamp
429                 t->getBufferHeadTimestamp(&ts_head, &fc_head);
430                 t->getBufferTailTimestamp(&ts_tail, &fc_tail);
431                 debugOutput(DEBUG_LEVEL_NORMAL,
432                         " TS after write: HEAD: %011llu, FC=%04u\n",
433                         ts_head,fc_head);
434                 debugOutput(DEBUG_LEVEL_NORMAL,
435                         "                 TAIL: %011llu, FC=%04u\n",
436                         ts_tail,fc_tail);
437
438                 // read one packet
439                 t->readFrames(arguments.frames_per_packet, (char *)&dummyframe_out);
440
441                 // read the buffer head timestamp
442                 t->getBufferHeadTimestamp(&ts_head, &fc_head);
443                 t->getBufferTailTimestamp(&ts_tail, &fc_tail);
444                 debugOutput(DEBUG_LEVEL_NORMAL,
445                         " TS after write: HEAD: %011llu, FC=%04u\n",
446                         ts_head,fc_head);
447                 debugOutput(DEBUG_LEVEL_NORMAL,
448                         "                 TAIL: %011llu, FC=%04u\n",
449                         ts_tail,fc_tail);
450
451                 // check
452                 bool pass=true;
453                 for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
454                     pass = pass && (dummyframe_in[i]==dummyframe_out[i]);
455                 }
456                 if (!pass) {
457                     debugOutput(DEBUG_LEVEL_NORMAL, "write/read check for cycle %d failed\n",cycle);
458                 }
459
460                 // update the timestamp
461                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
462                 if (timestamp >= arguments.wrap_at) {
463                     timestamp -= arguments.wrap_at;
464                 }
465             }
466
467             // simulate the cycle timer clock in ticks
468             time += 3072;
469             if (time >= arguments.wrap_at) {
470                 time -= arguments.wrap_at;
471             }
472
473             // allow for the messagebuffer thread to catch up
474             usleep(200);
475
476             if(!run) break;
477         }
478     }
479
480     // second run, now do block processing
481     debugOutput(DEBUG_LEVEL_NORMAL, "Start block read test...\n");
482     {
483         unsigned int blocksize=32;
484         int dummyframe_out_block[arguments.events_per_frame*arguments.frames_per_packet*blocksize];
485         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
486
487         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
488             dummyframe_in[i]=i;
489         }
490
491         uint64_t time=arguments.start_at_cycle*3072;
492
493         // initialize the timestamp
494         uint64_t timestamp=time;
495         if (timestamp >= arguments.wrap_at) {
496             // here we need a modulo because start_at_cycle can be large
497             timestamp %= arguments.wrap_at;
498         }
499         t->setBufferTailTimestamp(timestamp);
500
501         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
502         if (timestamp >= arguments.wrap_at) {
503             timestamp -= arguments.wrap_at;
504         }
505
506         for(unsigned int cycle=arguments.start_at_cycle;
507             cycle < arguments.start_at_cycle+arguments.total_cycles;
508             cycle++) {
509
510             // simulate the rate adaptation
511             int64_t diff=(time%arguments.wrap_at)-timestamp;
512
513             if (diff>(int64_t)arguments.wrap_at/2) {
514                 diff -= arguments.wrap_at;
515             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
516                 diff += arguments.wrap_at;
517             }
518
519             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
520
521             if(diff>0) {
522                 uint64_t ts_head, fc_head;
523                 uint64_t ts_tail, fc_tail;
524
525                 // write one packet
526                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
527
528                 // read the buffer head timestamp
529                 t->getBufferHeadTimestamp(&ts_head, &fc_head);
530                 t->getBufferTailTimestamp(&ts_tail, &fc_tail);
531                 debugOutput(DEBUG_LEVEL_NORMAL,
532                         " TS after write: HEAD: %011llu, FC=%04u\n",
533                         ts_head,fc_head);
534                 debugOutput(DEBUG_LEVEL_NORMAL,
535                         "                 TAIL: %011llu, FC=%04u\n",
536                         ts_tail,fc_tail);
537
538                 if (fc_head > blocksize) {
539                     debugOutput(DEBUG_LEVEL_NORMAL,"Reading one block (%u frames)\n",blocksize);
540
541                     // read one block
542                     t->readFrames(blocksize, (char *)&dummyframe_out_block);
543
544                     // read the buffer head timestamp
545                     t->getBufferHeadTimestamp(&ts_head, &fc_head);
546                     t->getBufferTailTimestamp(&ts_tail, &fc_tail);
547                     debugOutput(DEBUG_LEVEL_NORMAL,
548                             " TS after read: HEAD: %011llu, FC=%04u\n",
549                             ts_head,fc_head);
550                     debugOutput(DEBUG_LEVEL_NORMAL,
551                             "                TAIL: %011llu, FC=%04u\n",
552                             ts_tail,fc_tail);
553                 }
554
555                 // update the timestamp
556                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
557                 if (timestamp >= arguments.wrap_at) {
558                     timestamp -= arguments.wrap_at;
559                 }
560             }
561
562             // simulate the cycle timer clock in ticks
563             time += 3072;
564             if (time >= arguments.wrap_at) {
565                 time -= arguments.wrap_at;
566             }
567
568             // allow for the messagebuffer thread to catch up
569             usleep(200);
570
571             if(!run) break;
572         }
573     }
574
575     delete t;
576     delete c;
577
578     return EXIT_SUCCESS;
579 }
580
581
Note: See TracBrowser for help on using the browser.