sobota, 1 listopada 2014

Fundamental types initialization

Consider something as complicated as:

struct A
{
   int f; 
};

the problem is value of member 'f'.
Structure A is of course POD therefore rules for member initialization can be really tricky.
Differences comes from three sources:
- way of initalization,
- C++ standard version,
- compiler vendor.

Possible ways of initialization are:
A;
A();
A{};
which should be same to version with dynamic allocation:
new A;
new A();
new A{};

Note two things:
- it is impossible to use second case directly i.e.:
A a();
because in C++ this is function declaration (declaration can appear also inside function body).
To use second initialization method, syntax can be:
A a = A();
- third way of initialization is from C++11 and allows for:
A a{};

Initialization of fundamental type variables and PODs is subject of constant changes and obscurity between C++ standards.
In C++98 initialization of non-POD object without constructor (e.g. struct with desctructor only or inherited) does not initialize fundamental type fields to 0 (but as it is visible below it is not truth for current compilers).
This was changed in C++03.
Also compilers can introduce surprising behaviours. VSC++ (before version 2013) was famous of not initializing PODs with A() syntax (still visible for some cases).

Below are test results of initialization results for different types and compilers.
Cases are
  1. fundamental type,
  2. simplest POD struct,
  3. simplest non-POD struct (destructor added),
  4. non-POD struct with field default initialization in constructor,
  5. non-POD struct without field initialization in constructor,
  6. non-POD struct with inheritance and no constructors,
  7. non-POD struct with inheritance and field default initialization in base class constructor,
  8. non-POD struct with inheritance and field default initialization in derived class constructor.

Compilers used
  • gcc 4.8.2
  • clang 3.3
  • VisualStudio C++ 2013

1. fundamental type
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
int a; - - - - - - -
int a = int(); 0 0 0 0 0 0 0
int a{}; 0 n.a. n.a. 0 n.a. n.a. 0
int *a = new int; - - - - - - -
int *a = new int(); 0 0 0 0 0 0 0
int *a = new int{}; 0 n.a. n.a. 0 n.a. n.a. 0


2. simplest POD struct
struct A
{
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; - - - - - - -
A a = A(); 0 0 0 0 0 0 0
A a{}; 0 n.a. n.a. 0 n.a. n.a. 0
A *a = new A; - - - - - - -
A *a = new A(); 0 0 0 0 0 0 0
A *a = new A{}; 0 n.a. n.a. 0 n.a. n.a. 0


3. simplest non-POD struct (destructor added)
struct A
{
   ~A() {}
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; - - - - - - -
A a = A(); 0 0 0 0 0 0 -
A a{}; 0 n.a. n.a. 0 n.a. n.a. 0
A *a = new A; - - - - - - -
A *a = new A(); 0 0 0 0 0 0 -
A *a = new A{}; 0 n.a. n.a. 0 n.a. n.a. 0


4. non-POD struct with field default initialization in constructor
struct A
{
   A() : f() {}
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; 0 0 0 0 0 0 0
A a = A(); 0 0 0 0 0 0 0
A a{}; 0 n.a. n.a. 0 n.a. n.a. 0
A *a = new A; 0 0 0 0 0 0 0
A *a = new A(); 0 0 0 0 0 0 0
A *a = new A{}; 0 n.a. n.a. 0 n.a. n.a. 0


5. non-POD struct without field initialization in constructor
struct A
{
   A() {}
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; - - - - - - -
A a = A(); - - - - - - -
A a{}; - n.a. n.a. - n.a. n.a. -
A *a = new A; - - - - - - -
A *a = new A(); - - - - - - -
A *a = new A{}; - n.a. n.a. - n.a. n.a. -


6. non-POD struct with inheritance and no constructors
struct P
{
   int g;
};
struct A : public P
{
   int f;
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:-
A a = A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:-, g:-
A a{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:-
A *a = new A; f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:-
A *a = new A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0
A *a = new A{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:-


7. non-POD struct with inheritance and field default initialization in base class constructor
struct P
{
   P() : g() {}
   int g;
};
struct A : public P
{
   int f;
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0
A a = A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:-, g:0
A a{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:0
A *a = new A; f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0
A *a = new A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:-, g:0
A *a = new A{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:0


8. non-POD struct with inheritance and field default initialization in derived class constructor
struct P
{
   int g;
};
struct A : public P
{
   A() : f() {}
   int f;
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A a = A(); f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A a{}; f:0, g:- n.a. n.a. f:0, g:- n.a. n.a. f:0, g:-
A *a = new A; f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A *a = new A(); f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A *a = new A{}; f:0, g:- n.a. n.a. f:0, g:- n.a. n.a. f:0, g:-


Legend
  • "0" - initialized to 0
  • "-" - not initialized
  • "n.a." - available only in C++11

And what lesson comes form data above - to be really sure field is initialized, you have to explicitly use initialization construct for the field (in constructor initialization list).

Test application available for downlowad here. Please note placement new used to avoid false 0-positive answers. For stack allocated items it is not that easy - there is try with recursive function (to pollute stack with non 0 values) but it is not always enough (false 0 answers can happen).

Note that in VSC++2013 "__cplusplus" is defined as 199711 (like for C++98 standard) but compiler allows for C++11 usage (perhaps not complete and therefore "__cplusplus" is not defined as 201103).

wtorek, 19 sierpnia 2014

Acoustics absorption calculation (impedance tube)

Here is bunch of equations to calculate acoustic absorption coefficient according to transfer function method (ISO 10534-2:1998).
Absorption coefficient as function of frequency can be calculated from pressure reflection factor with:

Reflection factor is calculated from calculation (corrected) transfer function:
where 's' is microphones' spacing, 'x1' is distance form specimen to closer microphone and 'k0' is wave number.

Calculation (corrected) transfer function comes from:

Correction transfer function is obtained from:

and calculated as:

Please note that absorption coefficient calculation does not really need 'x1' distance to be known.
This is visible when following substitution is used in equation for reflection factor:

Then square of absolute value is given as (which is free from 'x1'):

Taking above into account ready to use equation can be derived:

taking following into account:

can be simplified to:

where:


Note that calculations are a bit simpler using polar representation for complex numbers.

niedziela, 3 sierpnia 2014

Real time (stream) acquisition with ADS1278 and MMB0 (ADS1278EVM-PDK)

MMB0 with ADS1278 board is perfect tool to start adventure with DSP programming, especially in A/D conversion context.

Solution presented below is development of first Linux approach presented here.

ADS1278EVM-PDK is delivered with ready to use Windows software called ADCPro which is post processing tool for data gathered. The tool is build using LabVIEW environment and allows for basic estimation of A/D converter features and performance.
Main weakness of ADCPro (which is of course out of its original purpose) is lack of data streaming handling. Data acquisition is realized within MMB0 using its 16MB SDRAM memory. After acquisition is finished, data is transferred to PC for further analysis and presentation. This of course limits length of data that can be acquired in single measurement.
It is tempting idea to create alternative software to provide data streaming from MMB0 functionality. Obvious limitation here is USB 1.1 used in TMS320VC5509A which drives MMB0 (Rev.D in my case).
For USB 1.1 bandwidth is practically limited to about 1MB/s, which does not allow for all 8 channels utilisation with high sample rate. But this limitation makes the problem even more attractive - try to get all possible performance from MMB0 and check limits of USB 1.1.

Prepared solution contains two parts: firmware for MMB0 prepared with free Linux version of CCS and simple PC (Linux) app for control and data acquisition.
In order to limit usage of precious bandwidth data samples transferred to PC are 24-bit length (1/4 less than original 32-bit samples). As 24-bit is native resolution of ADS1278 therefore this has no impact on measurement precision (no real downsampling).
Also transfers are performed using maximal possible buffer size for BULK transfers (close to TMS320VC5509A's limit 64kB) which gives maximal performance.
As MMB0 has 16MB of SDRAM memory it gives additional advantage when handling short time gathering even for sampling rates out of USB 1.1 transfer possibilities.
SDRAM used as cyclic buffer stores data before transfer and in case of overflow acquisition is stopped, but gathered data can be transferred to PC. Please also note that due to transfer being parallel to acquisition real acquisition length is more than SDRAM size (some data is transferred and additional acquisitions can be stored before overflow takes place).
As SDRAM buffer usage is critical parameter for application performance, MMB0's led segment is used to present digit which represents decimals of buffer usage (and letter 'o' in case of overflow).


Fig.1 MMB0 with ADS1278 board - buffer usage 0-9%

To gather data transferred using USB interface, simple app was developed. The app handles start and stop commands and stores gathered data to a file.
Application parameters allows for easy setup i.e. to specify cpu clock (via APLL divider and multiplier), 5509 output clock divider, also settings for external PLL (for ADS1278 clock) shall be specified (multiplier and dividers). The settings allows for arbitrary sample rate selection.
Beside clocks ADS operating settings shall be specified i.e. CLKDIV 1 or 0 and mode (highspeed, highresolution, lowpower or lowspeed).


Fig.2 Acquired sine wave signal for channel 4

Here are results obtained for maximal transfer rates for each mode (acquisition time before overflow occurs)

  • High-speed mode, 8ch, 144500 SPS, cpu_freq: 192 MHz (m: 16, d: 1, div: 2), ADS f_clk=36.992 MHz (p: 289, q: 75, post: 10)
    max acquisition time ~6.5 sec, acquired 22866480 bytes (in 21.8 sec)
  • High-resolution mode, 8ch, 52725 SPS, cpu_freq: 192 MHz (m: 16, d: 1, div: 4), ADS f_clk=26.9952 MHz (p: 703, q: 125, post: 10)
    max acquisition time ~70 sec, acquired 89107200 bytes (in 85.7 sec)
  • Low-power mode (CLKDIV = 1), 8ch, 52725 SPS, cpu_freq: 192 MHz (m: 16, d: 1, div: 4), ADS f_clk=26.9952 MHz (p: 703, q: 125, post: 10)
    max acquisition time ~63 sec, acquired79541280 bytes (in 78.6 sec)
  • Low-power mode (CLKDIV = 0), 8ch, 52725 SPS, cpu_freq: 192 MHz (m: 16, d: 1, div: 4), ADS f_clk=13.4976 MHz (p: 703, q: 100, post: 25)
    max acquisition time ~63 sec, acquired 79541280 bytes (in 78.5 sec)
  • Low-speed mode (CLKDIV = 1), 8ch, 10545 SPS, cpu_freq: 192 MHz (m: 16, d: 1, div: 4), ADS f_clk=26.9952 MHz (p: 703, q: 125, post: 10)
    no overflow occurred, continuous streaming possible, average transfer rate ~250kB/s
  • Low-speed mode (CLKDIV = 0), 8ch, 10545 SPS, cpu_freq: 192 MHz (m: 16, d: 1, div: 4), ADS f_clk=5.39904 MHz (p: 703, q: 125, post: 50)
    no overflow occurred, continuous streaming possible, average transfer rate ~250kB/s


Generally maximal sampling which can be performed without overflow (or with overflow after long time) is about 45kSPS (for 8 channels).


Please note that other devices connected to USB host may influence bandwidth for transfer. Also I have noticed with my computer that USB 2.0 host has better max throughput for USB 1.1 connection than USB 3.0 host.


TODO list:
  1. Add downsampling to 16-bit option for further bandwidth save and higher sampling rates handling.
  2. Add possibility to limit number of (or select) channels transferred to have even more bandwidth saving.
  3. Implement as Linux (and Windows) driver.

poniedziałek, 6 stycznia 2014

gcc link-time optimization

Link optimization can give impressive results (at least regarding executable size as presented below).
Here is output from simple program build. The program consists of 3 modules and 2 header files (6 functions in total).
  • Normal compilation (and linking), no optimization 'gcc mod1.c mod2.c mod3.c -o test'
    stripped executable size - 14480.
  • Normal compilation (and linking), optimized for size 'gcc mod1.c mod2.c mod3.c -o test -Os'
    stripped executable size - 10384.
  • LTO compilation (and linking), no optimization 'gcc mod1.c mod2.c mod3.c -o test -flto'
    stripped executable size - 10384.
  • LTO compilation (and linking), optimized for size 'gcc mod1.c mod2.c mod3.c -o test -flto -Os'
    stripped executable size - 6288.
GCC version used 4.8.2 (x86-64).

Please note that compilation time and output code speed was not taken into account in simple example consideration above.
Also for sure example is far too simple to treat it as meaningful case, please treat it just as remark of LTO possibilities.

References

  1. gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-flto-934
  2. gcc.gnu.org/wiki/summit2010?action=AttachFile&do=get&target=hubicka.pdf
  3. en.wikipedia.org/wiki/Link-time_optimization