Partie HW faite a peu près

This commit is contained in:
hackens-bot 2025-01-16 13:42:30 +01:00
parent 3c053bf278
commit 524ec7bf09
4 changed files with 195 additions and 99 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
Microphone as source follower. 175uA DC @ ~3V Vds
X = [62.0, 68.0, 75.0, 82.0, 91.0, 100.0, 110.00000000000001, 120.0, 130.0, 150.0, 160.0, 180.0, 200.0, 220.00000000000003, 240.0, 270.0, 300.0, 330.0, 360.0, 390.0, 430.0, 470.0, 509.99999999999994, 560.0, 620.0, 680.0, 750.0, 819.9999999999999, 910.0, 1000.0, 1100.0, 1200.0, 1300.0, 1500.0, 1600.0, 1800.0, 2000.0, 2200.0, 2400.0, 2700.0, 3000.0, 3300.0, 3600.0, 3900.0, 4300.0, 4700.0, 5100.0, 5600.0, 6200.0, 6800.0, 7500.0, 8200.0, 9100.0, 10000.0, 11000.0, 12000.0, 13000.0, 15000.0, 16000.0, 18000.0, 20000.0]
H = [(0.47012032175840673+0.01812983402014712j), (0.470773859825163+0.01694020487348213j), (0.47014391070243977+0.015085538991270814j), (0.47015580025611164+0.01290140838256883j), (0.4707965853705939+0.012540456593345836j), (0.4702655782670737+0.01146515427066669j), (0.47001958941149374+0.009066724097094081j), (0.4698897845061736+0.008590954654956465j), (0.4703328317106061+0.007680708662404258j), (0.47059597197084874+0.006947035949722949j), (0.4704696033876575+0.00577097334067109j), (0.4706101838316291+0.004496916581939091j), (0.4700645514757954+0.004640777774023917j), (0.46970384793911085+0.0038522275542666844j), (0.47072172644838994+0.004356868460710158j), (0.46985620214068086+0.0027604993703299464j), (0.4706992493463072+0.002204339769185796j), (0.47106305332005566+0.0021205228985848017j), (0.47028285448669327+0.0018612810692497153j), (0.4691514968724328+0.002292957901098495j), (0.4701873122263507+0.0002420230304226658j), (0.4689285261898509+0.0007289920633161568j), (0.46956552462586804-0.0007236174387345418j), (0.4678562305849072-0.0006907393785490914j), (0.46836755615019-0.0006038427857997494j), (0.4689235574748159-0.0016181768655454802j), (0.46876318278299123-0.0017764947735754034j), (0.4683097999229312-0.0027640827428271906j), (0.46786090676077974-0.002610856644027095j), (0.46753731928396514-0.002241714250446953j), (0.46801984732360963-0.002119721318204412j), (0.4663227653685854-0.0016727148025411536j), (0.4663031260029301-0.0022085460059039328j), (0.4658401896506971-0.0020177518020752486j), (0.46612388968980756-0.002207265245165768j), (0.4645078371101538-0.0028048732307074937j), (0.46394264877360886-0.0021639899863904162j), (0.4642719966632143-0.0015964764527535004j), (0.4632949784333158-0.0018359364430161399j), (0.46339335082824507-0.0007557123371271958j), (0.46374916001466004-0.0006997385251981024j), (0.46251209195262455-0.00011522690154944387j), (0.46177373985629666+0.0005354488426995059j), (0.4630923556768662+0.0009618057371807924j), (0.46250026655427934+0.002019603274076546j), (0.46115066695013796+0.002618340722087239j), (0.4599367294994153+0.001487858985361092j), (0.45947368133692446+0.0026319865902357893j), (0.4607616438306012+0.004473565837816463j), (0.46123373027180997+0.0039563877327196545j), (0.4612432999999601+0.004773317207737592j), (0.46113397679743406+0.006135235798210173j), (0.46122910037870635+0.005727347168252464j), (0.46150279974005726+0.007471744392586996j), (0.46260297120248955+0.007760981385780426j), (0.461474728572703+0.010231892504492484j), (0.46216777324979+0.009433273729074564j), (0.46091102876674295+0.010621600555034331j), (0.46093670915980167+0.012440290976269985j), (0.46216882884840704+0.014888096834763371j), (0.46407411135079274+0.015274025203592433j)]
We get a ~12kOhm output impedance, and negligible output capacitance.

View file

@ -25,130 +25,169 @@ fade_time = 0
def audio_callback(in_data, frame_count, time_info, status):
global fade_time
global change_f
global new_f
global current_phase
global normalized_f
global trans_done
global fade_time
global change_f
global new_f
global current_phase
global normalized_f
global trans_done
if change_f == True:
change_f = False
trans_done = False
fade_time = 0
if change_f == True:
change_f = False
trans_done = False
fade_time = 0
if(fade_time != 2*trans_len):
if(fade_time < trans_len):
last_fade = fade_time + frame_count
envelope = outro[fade_time:last_fade]
fade_time = min(last_fade, trans_len)
elif(fade_time >= trans_len and fade_time < 2*trans_len):
normalized_f = new_f
if(new_f == 0):
current_phase = 0. # Avoid weird sub-audio behavior
last_fade = fade_time + frame_count
envelope = intro[fade_time - trans_len:last_fade - trans_len]
fade_time = min(last_fade, 2*trans_len)
if(fade_time != 2*trans_len):
if(fade_time < trans_len):
last_fade = fade_time + frame_count
envelope = outro[fade_time:last_fade]
fade_time = min(last_fade, trans_len)
elif(fade_time >= trans_len and fade_time < 2*trans_len):
normalized_f = new_f
if(new_f == 0):
current_phase = 0. # Avoid weird sub-audio behavior
last_fade = fade_time + frame_count
envelope = intro[fade_time - trans_len:last_fade - trans_len]
fade_time = min(last_fade, 2*trans_len)
if(fade_time == 2*trans_len):
trans_done = True
if(fade_time == 2*trans_len):
trans_done = True
sine = np.sin(2 * np.pi * (current_phase + np.arange(frame_count)*normalized_f))
delta = frame_count*normalized_f
delta -= np.floor(delta)
sine = np.sin(2 * np.pi * (current_phase + np.arange(frame_count)*normalized_f))
delta = frame_count*normalized_f
delta -= np.floor(delta)
if(delta + current_phase > 1):
current_phase += delta - 1
else:
current_phase += delta
if(delta + current_phase > 1):
current_phase += delta - 1
else:
current_phase += delta
if(fade_time != 2*trans_len):
sine *= envelope
if(fade_time != 2*trans_len):
sine *= envelope
return (sine.astype(np.float32).tobytes(), pa.paContinue)
return (sine.astype(np.float32).tobytes(), pa.paContinue)
paudio = pa.PyAudio()
audio_strm = paudio.open(format=pa.paFloat32,
channels=1,
rate=44100,
output=True,
input=False,
stream_callback=audio_callback,
start=True)
channels=1,
rate=44100,
output=True,
input=False,
stream_callback=audio_callback,
start=True)
print(paudio.get_default_output_device_info())
print(audio_strm.is_active())
def acquire_samples(sc_frequency, oscillo):
global new_f
global change_f
global trans_done
new_f = sc_frequency
change_f = True
while (change_f == True) or (trans_done == False):
time.sleep(0.05)
time.sleep(0.2)
global new_f
global change_f
global trans_done
oscillo.write("acquire:state run")
oscillo.write("data:source CH1")
input_ch = oscillo.query_binary_values("curve?",datatype="b",container=np.array).astype(np.float64)
oscillo.write("data:source CH2")
output_ch = oscillo.query_binary_values("curve?",datatype="b",container=np.array).astype(np.float64)
new_f = sc_frequency
change_f = True
while (change_f == True) or (trans_done == False):
time.sleep(0.01)
time.sleep(0.1)
return input_ch, output_ch
oscillo.write("acquire:state run")
oscillo.write("data:source CH1")
input_ch = oscillo.query_binary_values("curve?",datatype="b",container=np.array).astype(np.float64)
oscillo.write("data:source CH2")
output_ch = oscillo.query_binary_values("curve?",datatype="b",container=np.array).astype(np.float64)
return input_ch, output_ch
def manage_measures(f_list, oscillo): # These are NOT normalized frequencies
hscales = [0.25, 100e-3, 50e-3, 25e-3, 10e-3, 5e-3, 2.5e-3, 1e-3, 500e-6, 250e-6, 100e-6, 50e-6, 25e-6, 10e-6]
hscales_str = ["2.5e-1", "1.0e-1", "5.0e-2", "2.5e-2", "1.0e-2", "5.0e-3", "2.5e-3", "1.0e-3", "5.0e-4", "2.5e-4", "1.0e-4", "5.0e-5", "2.5e-5", "1.0e-5"]
hscales = [0.25, 100e-3, 50e-3, 25e-3, 10e-3, 5e-3, 2.5e-3, 1e-3, 500e-6, 250e-6, 100e-6, 50e-6, 25e-6, 10e-6]
hscales_str = ["2.5e-1", "1.0e-1", "5.0e-2", "2.5e-2", "1.0e-2", "5.0e-3", "2.5e-3", "1.0e-3", "5.0e-4", "2.5e-4", "1.0e-4", "5.0e-5", "2.5e-5", "1.0e-5"]
hscale_index = 0
current_hscale = hscales[hscale_index]
hscale_index = 0
current_hscale = hscales[hscale_index]
cal_A = []
cal_B = []
cal_done = False
H = []
vscales = [0.25, 100e-3, 50e-3, 25e-3]
vscales_str = ["2.5e-1", "1.0e-1", "5.0e-2", "2.5e-2"]
vscale_index = 0
current_vscale = vscales[vscale_index]
W = sc.signal.windows.flattop(2500)
cal_A = []
cal_B = []
cal_done = False
for f in f_list:
sc_frequency = np.round(2**20/44100 * f)/2**20
num_periods = f * 10 * current_hscale
while(num_periods >= 5 * 2.5):
hscale_index += 1
current_hscale = hscales[hscale_index]
num_periods = f * 10 * current_hscale
print("horizontal:main:scale " + hscales_str[hscale_index])
oscillo.write("horizontal:main:scale " + hscales_str[hscale_index])
cal_done = False
H = []
print(f, " : ", current_hscale,"s/div, ", num_periods, " periods")
A, B = acquire_samples(sc_frequency, oscillo)
if not cal_done:
cal_A, cal_B = acquire_samples(0., oscillo)
cal_done = True
print(A)
print(B)
A, B = (A - cal_A)*W, (B - cal_B)*W
f_acq = 250./current_hscale
rel_f = f / f_acq
W = sc.signal.windows.flattop(2500)
# Obtain the Fourier transforms of A, B @ rel_f, with flattop windowing
V = np.exp(-2.0j * np.pi * np.arange(2500) * rel_f)
h = np.sum(B*V)/np.sum(A*V)
print("H(", f, ") = ", h)
H.append(h)
return H
for f in f_list:
sc_frequency = np.round(2**20/44100 * f)/2**20
num_periods = f * 10 * current_hscale
update_hscale = False
while(num_periods >= 5 * 2.5):
hscale_index += 1
current_hscale = hscales[hscale_index]
num_periods = f * 10 * current_hscale
update_hscale = True
if update_hscale:
print("horizontal:main:scale " + hscales_str[hscale_index])
oscillo.write("horizontal:main:scale " + hscales_str[hscale_index])
cal_done = False
print(f, " : ", current_hscale,"s/div, ", num_periods, " periods")
A, B = acquire_samples(sc_frequency, oscillo)
is_sat, is_under = False, True
for x in B:
is_sat |= (abs(x) >= 127)
is_under &= ~(abs(x) >= 64)
while((is_sat and vscale_index != 0) or (is_under and vscale_index != len(vscales)-1)):
if(is_sat):
print("We have saturation")
vscale_index-=1
elif(is_under):
print("We are wasting bits")
vscale_index+=1
current_vscale = vscales[vscale_index]
osci.write("ch2:scale " + vscales_str[vscale_index])
cal_done = False
A, B = acquire_samples(sc_frequency, oscillo)
is_sat, is_under = False, True
for x in B:
is_sat |= (abs(x) >= 120)
is_under &= ~(abs(x) >= 64)
if not cal_done:
cal_A, cal_B = acquire_samples(0., oscillo)
cal_done = True
print(A)
print(B)
A, B = (A - cal_A)*W, (B - cal_B)*W
f_acq = 250./current_hscale
rel_f = f / f_acq
B *= current_vscale/vscales[0]
# Obtain the Fourier transforms of A, B @ rel_f, with flattop windowing
V = np.exp(-2.0j * np.pi * np.arange(2500) * rel_f)
h = np.sum(B*V)/np.sum(A*V)
print("H(", f, ") = ", h)
H.append(h)
return H
def find_oscillo():
rm = pyvisa.ResourceManager()
R = rm.list_resources()
print(R)
# if R.empty():
# print("No device found")
# exit(1)
return rm.open_resource(R[0])
rm = pyvisa.ResourceManager()
R = rm.list_resources()
print(R)
# if R.empty():
# print("No device found")
# exit(1)
return rm.open_resource(R[0])
osci = find_oscillo()
@ -176,17 +215,53 @@ osci.write("trigger:main:type EDGE")
osci.write("acquire:mode sample")
osci.write("acquire:stopafter sequence")
osci.write("data:encdg RIBinary")
decade = [1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2]
X = [68., 82] + list(np.array(decade)*([100.]*len(decade))) + list(np.array(decade) * ([1000.]*len(decade))) + [1e4, 1.2e4, 1.5e4, 1.8e4]
def build_spoints(decade):
res = []
power_of_ten = 10
while(power_of_ten <= 100000):
for x in decade:
f = x * power_of_ten
if(60. <= f and f <= 20000.):
res.append(f)
power_of_ten *= 10
return res
e12 = [1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2]
e24 = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1]
e48 = [1.00, 1.05, 1.10, 1.15, 1.21, 1.27, 1.33, 1.40, 1.47, 1.54, 1.62, 1.69, 1.78, 1.87, 1.96, 2.05, 2.15, 2.26, 2.37, 2.49, 2.61, 2.74, 2.87, 3.01, 3.16, 3.32, 3.48, 3.65, 3.83, 4.02, 4.22, 4.42, 4.64, 4.87, 5.11, 5.36, 5.62, 5.90, 6.19, 6.49, 6.81, 7.15, 7.50, 7.87, 8.25, 8.66, 9.09, 9.53]
X = build_spoints(e24)
print(X)
H = manage_measures(X, osci)
Y = 20 * np.log10(np.abs(H))
phase = np.angle(H, deg=True)
for i in range(1, len(phase)):
while(abs(phase[i] - phase[i - 1]) >= 180):
if(phase[i] >= phase[i-1]):
phase[i] -= 360
else:
phase[i] += 360
print(X)
print(H)
print("Corrected phase/magnitude :")
print(phase)
print(Y)
fig, axes = plt.subplots(nrows = 2, ncols=1, sharex = True)
axes[0].semilogx(X,Y)
axes[0].grid()
axes[1].semilogx(X, np.angle(H, deg=True))
axes[1].semilogx(X, phase)
axes[1].grid()
plt.show(block = False)
plt.figure(2)
plt.plot(X, phase)
plt.show()