1#!/usr/bin/python
2
3# ----------------------------------------------------------------------------
4#
5#  Copyright (C) 2013-2020 Fons Adriaensen <fons@linuxaudio.org>
6#
7#  This program is free software; you can redistribute it and/or modify
8#  it under the terms of the GNU General Public License as published by
9#  the Free Software Foundation; either version 3 of the License, or
10#  (at your option) any later version.
11#
12#  This program is distributed in the hope that it will be useful,
13#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15#  GNU General Public License for more details.
16#
17#  You should have received a copy of the GNU General Public License
18#  along with this program.  If not, see <http:#www.gnu.org/licenses/>.
19#
20# ----------------------------------------------------------------------------
21
22
23import sys
24import matplotlib.pyplot as plt
25import numpy as np
26from time import sleep
27from math import hypot, log10
28from jacktools.jacksignal import JackSignal
29sys.path.append ('..')
30from utils.sinewave import *
31
32
33# --------------------- IEC Intermodulation measurement ----------------------
34#
35# Run jack_nonlin with arguments 0.01 0.01 0
36
37
38# We need a linear X axis for the bargraph.
39#
40LogF = np.linspace (2.7, 4.3, 17, endpoint = True)
41Freq = np.power (10.0, LogF) # 500 Hz to 20 kHz, 1/3 oct.
42Fdiff = 60
43Level = -20
44Mtime = 1.0
45
46# Create a Jacksignal object.
47#
48J = JackSignal("IMtest")
49if J.get_state() < 0:
50    print ("Failed to create JackSignal -- is the server running ?")
51    exit(1)
52
53# Get Jack info.
54#
55name, Fsamp, period = J.get_jack_info()
56
57# Create ports and connect
58#
59J.create_output (0, "out")
60J.create_input (0, "in")
61J.silence()
62J.connect_output (0, "jack_nonlin:in")
63J.connect_input (0, "jack_nonlin:out")
64
65# Length of test signals in samples.
66#
67siglen = int (Mtime * Fsamp + 0.5)
68margin = 1000
69buflen = siglen + margin
70
71# Input buffer.
72#
73Ainp = np.zeros ((buflen,), dtype = np.float32)
74
75
76# Run test.
77#
78N = Freq.shape [0]
79D2 = N * [0]
80D3 = N * [0]
81for i in range (N):
82
83    # Generate test signal, will be scaled for output.
84    #
85    Fcent = Freq [i]
86    Flo = Fcent - Fdiff / 2
87    Fhi = Fcent + Fdiff / 2
88    amp = pow (10.0, Level / 20.0)
89    Aout = (  gen_sinewave (amp, Flo, Fsamp, buflen)
90            + gen_sinewave (amp, Fhi, Fsamp, buflen)).astype (np.float32)
91
92    # Generate reference signals for selective measurement.
93    #
94    Mlo = gen_complex (Flo, Fsamp, siglen)
95    Mhi = gen_complex (Fhi, Fsamp, siglen)
96    M3a = gen_complex (2 * Flo - Fhi, Fsamp, siglen)
97    M3b = gen_complex (2 * Fhi - Flo, Fsamp, siglen)
98    M2  = gen_complex (Fdiff, Fsamp, siglen)
99
100    # Define signal buffers and run test.
101    #
102    J.set_output_data (0, Aout)
103    J.set_input_data (0, Ainp)
104    J.process()
105    J.wait()
106
107    # Skip margin samples and measure.
108    # We are not interested in the phase here.
109    #
110    T = Ainp [margin:]
111    Llo,p = sigdetect (T, Mlo)
112    Lhi,p = sigdetect (T, Mhi)
113    L2,p  = sigdetect (T, M2)
114    L3a,p = sigdetect (T, M3a)
115    L3b,p = sigdetect (T, M3b)
116
117    # Print results.
118    #
119    dblo = 20 * log10 (Llo + 1e-20)
120    dbhi = 20 * log10 (Lhi + 1e-20)
121    D2 [i] = im2 = 100 * L2 / (Llo + Lhi)
122    D3 [i] = im3 = 100 * hypot (L3b, L3b) / (Llo + Lhi)
123    print ("Freq = %7.1f    Al = %5.1f  Ah = %5.1f   im2 = %6.3f%%  im3 = %6.3f%%" % (Fcent, dblo, dbhi, im2, im3))
124
125# Stop the Jacksignal process.
126del J
127
128# Create a nice graph to present the results.
129#
130fig = plt.figure (figsize=(8,6), facecolor='white')
131ax = fig.add_axes ([0.07, 0.05, 0.86, 0.90])
132ax.set_title ("% Intermodulation distortion (IEC)")
133ax.set_xlim (2.6, 4.4)
134ax.set_xticks ((2.7, 3.0, 3.3, 3.7, 4.0, 4.3))
135ax.set_xticklabels (('500', '1k', '2k', '5k', '10k', '20k'))
136ax.set_ylim (1e-3, 1e1)
137ax.set_yscale ('log')
138ax.bar (LogF - 0.022, D2, 0.02, color = 'b')
139ax.bar (LogF + 0.002, D3, 0.02, color = 'g')
140ax.text (2.75, 5.0, 'IM2', size = 17, color = 'b')
141ax.text (2.95, 5.0, 'IM3', size = 17, color = 'g')
142ax.grid ()
143plt.show ()
144
145
146