# TES-1350A Sound Level Meter with Python 目的:將分貝計連上電腦,獲取即時數據,數值需要與分貝計上顯示的一致。 ## Environment - [TES-1350A Sound Level Meter](https://www.tes.com.tw/product_detail.asp?seq=64) - [USB Audio Interface / USB 音效卡 / UGREEN USB to Audio Jack Sound Card Adapter](https://www.amazon.com/UGREEN-External-Headphone-Microphone-Desktops/dp/B01N905VOY?th=1) - 3.5mm audio plug - Macbook Air M2 ## TES-1350A - [英文說明書](https://d17bck4wpaw2mg.cloudfront.net/att/a/2/c/4/a2c463/tes1350a-manual.pdf) ### 摘要 > Time weighting: FAST(125mS), SLOW(1 sec.) > Level ranges: Lo: 35~100dB and Hi: 65~130dB > Accuracy: ± 2.0dB (under reference conditions) > Calibration control can be adjusted clockwise or counterclockwise to standard 94.0dB. > AC output: 0.65 Vrms at FS (full scale), output impedance approx. 600 Ω > DC output: 10mV / dB, output impedance approx. 100 Ω > Calibration: Electrical calibration with the internal oscillator (1KHz sine wave) ![IMG_6091-3-1](https://hackmd.io/_uploads/ryHUezHPA.jpg) ### 使用參數 - low range: Lo: 35 ~ 100dB - RESPONSE: F: fast respnse - FUNCT: A: A-weighting ## 使用 pyaudio - 輸出結果已經很接近分貝計上顯示的數值,但輸出的數字大概要再減個 2~4 dB 才會更接近。 ```python= import numpy as np import pyaudio import datetime import math # Constants from the TES-1350A manual V_fs = 0.65 # Full scale voltage in Vrms db_fs = 100 # Corresponding dB level at full scale in low range # Calculate the reference voltage for 94 dB SPL V_94 = V_fs * 10 ** ((94 - db_fs) / 20) def voltage_to_db_spl(v_rms, v_ref): return 20 * np.log10(v_rms / v_ref) + 94 def callback(indata, frames, time, status): indata = np.frombuffer(indata, dtype=np.int16) # Convert to 16-bit integer max_int_value = 2**15 # 32768 for 16-bit PCM # Scale the integer data to voltage voltage_data = indata / max_int_value * V_fs squared_data = np.square(voltage_data) mean_squared_data = np.mean(squared_data) rms_voltage = np.sqrt(mean_squared_data) # Calculate the RMS voltage of the input signal print(f"RMS Voltage: {rms_voltage:.6f} V", flush=True) # Convert the RMS voltage to decibels (A-weighted) db_spl = voltage_to_db_spl(rms_voltage, V_94) log_entry = f"{datetime.datetime.now()} - {db_spl:.2f} dB(A)\n" print(log_entry, end='') device_index = 2 # Replace with the actual device index duration = 1 # Replace with the actual duration in seconds FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 CHUNK = 1024 audio = pyaudio.PyAudio() stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, input_device_index=device_index, frames_per_buffer=CHUNK) print("Recording...") for _ in range(0, int(RATE / CHUNK * duration)): indata = stream.read(CHUNK) callback(indata, CHUNK, None, None) print("Finished recording.") stream.stop_stream() stream.close() audio.terminate() ``` ## ChatGPT 對程式碼的解釋 ### Information Used: 1. **Full Scale Voltage (V_fs)**: 0.65 Vrms as specified in the manual, which corresponds to the full-scale dB level (100 dB) on the meter. 2. **Full Scale dB Level (db_fs)**: 100 dB is the full-scale level for the low range. 3. **Calibration Reference (94 dB SPL)**: The device can be calibrated to 94 dB SPL, a standard reference point. ### Steps and Calculations: 1. **Convert Raw Audio Data to Integers**: - The audio data is captured in 16-bit PCM format, which ranges from -32768 to 32767. This raw data is converted to integers using `np.frombuffer(indata, dtype=np.int16)`. 2. **Scale the Integer Data to Voltage**: - The raw integer values are scaled to voltages based on the full-scale voltage (0.65 Vrms). The formula used is: $$ \text{voltage_data} = \frac{\text{indata}}{\text{max_int_value}} \times V_{fs} $$ - Here, `max_int_value` is \(2^{15} = 32768\), the maximum value for 16-bit PCM data. 3. **Calculate RMS Voltage**: - The Root Mean Square (RMS) voltage is calculated from the scaled voltage data: $$ \text{squared_data} = \text{voltage_data}^2 $$ $$ \text{mean_squared_data} = \text{mean}(\text{squared_data}) $$ $$ \text{rms_voltage} = \sqrt{\text{mean_squared_data}} $$ - This gives the RMS voltage of the input signal. 4. **Reference Voltage Calculation**: - Calculate the reference voltage \(V_{94}\) corresponding to 94 dB SPL: $$ V_{94} = V_{fs} \times 10^{\frac{94 - db_{fs}}{20}} $$ - This uses the relationship between voltage and dB SPL, where \(V_{fs}\) is the full-scale voltage and \(db_{fs}\) is the full-scale dB level. 5. **Convert RMS Voltage to dB SPL**: - The formula to convert the RMS voltage to dB SPL (A-weighted) is: $$ \text{dB SPL} = 20 \times \log_{10}\left(\frac{\text{rms_voltage}}{V_{94}}\right) + 94 $$ - This formula compares the measured RMS voltage to the reference voltage \(V_{94}\) and adds the calibration level (94 dB SPL). This ensures that the audio signal is correctly processed, scaled, and converted to dB SPL (A-weighted), matching the expected readings from your sound level meter. ## 使用 sounddevice :::warning 失敗,轉換出錯誤數值,程式碼有問題。 ::: ### 伏特轉換分貝 ```python= import numpy as np import sounddevice as sd import datetime # Constants from the TES-1350A manual V_fs = 0.65 # Full scale voltage in Vrms db_fs = 100 # Corresponding dB level at full scale in Hi range def voltage_to_db(v_rms, v_fs, db_fs): return (v_rms / v_fs) * db_fs def callback(indata, frames, time, status): # Flatten the array to a single dimension flat_indata = indata.flatten() # Calculate the RMS voltage of the input signal rms_voltage = np.sqrt(np.mean(flat_indata**2)) print(f"RMS Voltage: {rms_voltage:.6f} V", flush=True) # Convert the RMS voltage to decibels db = voltage_to_db(rms_voltage, V_fs, db_fs) log_entry = f"{datetime.datetime.now()} - {db:.2f} dB\n" print(log_entry, end='') device_index = 2 # Replace with the actual device index duration = 1 # Replace with the actual duration in seconds with sd.InputStream(callback=callback, device=device_index): sd.sleep(duration * 1000) ``` Output ``` RMS Voltage: 0.000063 V 2024-07-04 19:58:00.784316 - 0.01 dB RMS Voltage: 0.000065 V 2024-07-04 19:58:00.868526 - 0.01 dB RMS Voltage: 0.000062 V 2024-07-04 19:58:00.953830 - 0.01 dB ``` ### Raw data ```python= import sounddevice as sd import numpy as np import datetime def log_decibels(duration, device_index=2, filename='raw_output_signals.txt'): with open(filename, 'w') as f: def callback(indata, frames, time, status): log_entry = f"{datetime.datetime.now()} - {indata}\n" print(log_entry, end='') f.write(log_entry) with sd.InputStream(callback=callback, device=device_index): sd.sleep(duration * 1000) if __name__ == "__main__": log_decibels(1) ``` Output ``` 2024-07-05 14:08:30.426940 - [[-0.00125122] [-0.0010376 ] [-0.00128174] ... [ 0.00146484] [ 0.00088501] [ 0.00115967]] 2024-07-05 14:08:30.512188 - [[ 0.00149536] [ 0.00097656] [ 0.00018311] ... [-0.00112915] [-0.00057983] [-0.00057983]] 2024-07-05 14:08:30.597561 - [[-2.7465820e-04] [ 3.0517578e-05] [-1.8310547e-04] ... [ 6.1340332e-03] [ 8.3312988e-03] [ 7.7819824e-03]] ``` ## Reference - [錄音筆記-數位音量標準、 dBFS、dBTP、LUFS](https://corychu.medium.com/%E9%8C%84%E9%9F%B3%E7%AD%86%E8%A8%98-%E6%95%B8%E4%BD%8D%E9%9F%B3%E9%87%8F%E6%A8%99%E6%BA%96-dbfs-dbtp-lufs-c47ca4646b7f) - Github repo - https://github.com/johnson86tw/noise-monitoring-pi - https://github.com/johnson86tw/noise-monitoring