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): def audio_callback(in_data, frame_count, time_info, status):
global fade_time global fade_time
global change_f global change_f
global new_f global new_f
global current_phase global current_phase
global normalized_f global normalized_f
global trans_done global trans_done
if change_f == True: if change_f == True:
change_f = False change_f = False
trans_done = False trans_done = False
fade_time = 0 fade_time = 0
if(fade_time != 2*trans_len): if(fade_time != 2*trans_len):
if(fade_time < trans_len): if(fade_time < trans_len):
last_fade = fade_time + frame_count last_fade = fade_time + frame_count
envelope = outro[fade_time:last_fade] envelope = outro[fade_time:last_fade]
fade_time = min(last_fade, trans_len) fade_time = min(last_fade, trans_len)
elif(fade_time >= trans_len and fade_time < 2*trans_len): elif(fade_time >= trans_len and fade_time < 2*trans_len):
normalized_f = new_f normalized_f = new_f
if(new_f == 0): if(new_f == 0):
current_phase = 0. # Avoid weird sub-audio behavior current_phase = 0. # Avoid weird sub-audio behavior
last_fade = fade_time + frame_count last_fade = fade_time + frame_count
envelope = intro[fade_time - trans_len:last_fade - trans_len] envelope = intro[fade_time - trans_len:last_fade - trans_len]
fade_time = min(last_fade, 2*trans_len) fade_time = min(last_fade, 2*trans_len)
if(fade_time == 2*trans_len): if(fade_time == 2*trans_len):
trans_done = True trans_done = True
sine = np.sin(2 * np.pi * (current_phase + np.arange(frame_count)*normalized_f)) sine = np.sin(2 * np.pi * (current_phase + np.arange(frame_count)*normalized_f))
delta = frame_count*normalized_f delta = frame_count*normalized_f
delta -= np.floor(delta) delta -= np.floor(delta)
if(delta + current_phase > 1): if(delta + current_phase > 1):
current_phase += delta - 1 current_phase += delta - 1
else: else:
current_phase += delta current_phase += delta
if(fade_time != 2*trans_len): if(fade_time != 2*trans_len):
sine *= envelope sine *= envelope
return (sine.astype(np.float32).tobytes(), pa.paContinue) return (sine.astype(np.float32).tobytes(), pa.paContinue)
paudio = pa.PyAudio() paudio = pa.PyAudio()
audio_strm = paudio.open(format=pa.paFloat32, audio_strm = paudio.open(format=pa.paFloat32,
channels=1, channels=1,
rate=44100, rate=44100,
output=True, output=True,
input=False, input=False,
stream_callback=audio_callback, stream_callback=audio_callback,
start=True) start=True)
print(paudio.get_default_output_device_info()) print(paudio.get_default_output_device_info())
print(audio_strm.is_active()) print(audio_strm.is_active())
def acquire_samples(sc_frequency, oscillo): def acquire_samples(sc_frequency, oscillo):
global new_f global new_f
global change_f global change_f
global trans_done 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)
oscillo.write("acquire:state run") new_f = sc_frequency
oscillo.write("data:source CH1") change_f = True
input_ch = oscillo.query_binary_values("curve?",datatype="b",container=np.array).astype(np.float64) while (change_f == True) or (trans_done == False):
oscillo.write("data:source CH2") time.sleep(0.01)
output_ch = oscillo.query_binary_values("curve?",datatype="b",container=np.array).astype(np.float64) 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 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 = [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_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 hscale_index = 0
current_hscale = hscales[hscale_index] 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: H = []
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
print(f, " : ", current_hscale,"s/div, ", num_periods, " periods") W = sc.signal.windows.flattop(2500)
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
# Obtain the Fourier transforms of A, B @ rel_f, with flattop windowing for f in f_list:
V = np.exp(-2.0j * np.pi * np.arange(2500) * rel_f) sc_frequency = np.round(2**20/44100 * f)/2**20
h = np.sum(B*V)/np.sum(A*V) num_periods = f * 10 * current_hscale
print("H(", f, ") = ", h) update_hscale = False
H.append(h) while(num_periods >= 5 * 2.5):
return H 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(): def find_oscillo():
rm = pyvisa.ResourceManager() rm = pyvisa.ResourceManager()
R = rm.list_resources() R = rm.list_resources()
print(R) print(R)
# if R.empty(): # if R.empty():
# print("No device found") # print("No device found")
# exit(1) # exit(1)
return rm.open_resource(R[0]) return rm.open_resource(R[0])
osci = find_oscillo() osci = find_oscillo()
@ -176,17 +215,53 @@ osci.write("trigger:main:type EDGE")
osci.write("acquire:mode sample") osci.write("acquire:mode sample")
osci.write("acquire:stopafter sequence") osci.write("acquire:stopafter sequence")
osci.write("data:encdg RIBinary") 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) H = manage_measures(X, osci)
Y = 20 * np.log10(np.abs(H)) 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(H)
print("Corrected phase/magnitude :")
print(phase)
print(Y)
fig, axes = plt.subplots(nrows = 2, ncols=1, sharex = True) fig, axes = plt.subplots(nrows = 2, ncols=1, sharex = True)
axes[0].semilogx(X,Y) axes[0].semilogx(X,Y)
axes[0].grid() axes[0].grid()
axes[1].semilogx(X, np.angle(H, deg=True)) axes[1].semilogx(X, phase)
axes[1].grid() axes[1].grid()
plt.show(block = False)
plt.figure(2)
plt.plot(X, phase)
plt.show() plt.show()