Compare commits
6 Commits
4066899530
...
codex/2026
| Author | SHA1 | Date | |
|---|---|---|---|
| f1d844f6a3 | |||
| cb7f966081 | |||
| ecdad1b583 | |||
| 32fb12a2c2 | |||
| 12cf9d6716 | |||
| 8ca2fe39d0 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
132
_device_main.py
132
_device_main.py
@ -1,8 +1,10 @@
|
|||||||
from FreeSimpleGUI import TIMEOUT_KEY, WIN_CLOSED
|
from FreeSimpleGUI import TIMEOUT_KEY, WIN_CLOSED
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
import device_interaction as dev
|
import device_interaction as dev
|
||||||
|
|
||||||
@ -19,9 +21,22 @@ GUI_TIMEOUT_INTERVAL = 5#505 - dev.WAIT_AFTER_SEND*1000 # GUI refresh time in mi
|
|||||||
SAVE_POINTS_NUMBER = 1000 # Number of most recent data points kept in memory
|
SAVE_POINTS_NUMBER = 1000 # Number of most recent data points kept in memory
|
||||||
|
|
||||||
INITIAL_TEMPERATURE_1 = 28 # Set initial temperature for Laser 1 in Celsius: from -1 to 45 C ??
|
INITIAL_TEMPERATURE_1 = 28 # Set initial temperature for Laser 1 in Celsius: from -1 to 45 C ??
|
||||||
INITIAL_TEMPERATURE_2 = 28.9 # Set initial temperature for Laser 2 in Celsius: from -1 to 45 C ??
|
INITIAL_TEMPERATURE_2 = 29.2 # Set initial temperature for Laser 2 in Celsius: from -1 to 45 C ??
|
||||||
INITIAL_CURRENT_1 = 33 # 64.0879 max # Set initial current for Laser 1, in mA
|
INITIAL_CURRENT_1 = 33 # 64.0879 max # Set initial current for Laser 1, in mA
|
||||||
INITIAL_CURRENT_2 = 35 # 64.0879 max # Set initial current for Laser 2, in mA
|
INITIAL_CURRENT_2 = 30 # 64.0879 max # Set initial current for Laser 2, in mA
|
||||||
|
|
||||||
|
AD9833_FREQ_DEFAULT_KHZ = 1000
|
||||||
|
AD9833_MCLK_DEFAULT_MHZ = 20.0
|
||||||
|
DS1809_MAX_STEP = 63
|
||||||
|
DS1809_DEFAULT_STEP = 43
|
||||||
|
DS1809_INIT_HOME_PULSES = 64
|
||||||
|
DS1809_INIT_PULSE_MS = 2
|
||||||
|
DS1809_INIT_STARTUP_DELAY_S = 0.35
|
||||||
|
STM32_DAC_VREF = 2.5
|
||||||
|
STM32_DAC_MAX_CODE = 4095
|
||||||
|
PA4_DAC_DEFAULT_VOLT = 0.15
|
||||||
|
AD9102_WAVEFORM_FILE_NAME = "waveform.json"
|
||||||
|
AD9102_WAVEFORM_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), AD9102_WAVEFORM_FILE_NAME)
|
||||||
|
|
||||||
#### ---- Functions
|
#### ---- Functions
|
||||||
|
|
||||||
@ -73,6 +88,48 @@ def shorten(i):
|
|||||||
return "{:.2f}".format(round(i, 2))
|
return "{:.2f}".format(round(i, 2))
|
||||||
|
|
||||||
|
|
||||||
|
def clamp_int(value, min_value, max_value):
|
||||||
|
if value < min_value:
|
||||||
|
return min_value
|
||||||
|
if value > max_value:
|
||||||
|
return max_value
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def format_ds1809_status(step):
|
||||||
|
return f"{step}/{DS1809_MAX_STEP} шагов"
|
||||||
|
|
||||||
|
|
||||||
|
def voltage_to_dac_code(voltage_v):
|
||||||
|
if voltage_v is None:
|
||||||
|
return 0
|
||||||
|
if voltage_v < 0.0:
|
||||||
|
voltage_v = 0.0
|
||||||
|
if voltage_v > STM32_DAC_VREF:
|
||||||
|
voltage_v = STM32_DAC_VREF
|
||||||
|
return int(round((voltage_v / STM32_DAC_VREF) * STM32_DAC_MAX_CODE))
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_ds1809_position(prt, default_step):
|
||||||
|
default_step = clamp_int(int(default_step), 0, DS1809_MAX_STEP)
|
||||||
|
|
||||||
|
# Give the STM32 side a short startup margin before DS1809 pulse traffic.
|
||||||
|
time.sleep(DS1809_INIT_STARTUP_DELAY_S)
|
||||||
|
|
||||||
|
dev.send_ds1809_pulse(prt, uc=False, dc=True,
|
||||||
|
count=DS1809_INIT_HOME_PULSES,
|
||||||
|
pulse_ms=DS1809_INIT_PULSE_MS)
|
||||||
|
current_step = 0
|
||||||
|
|
||||||
|
if default_step > 0:
|
||||||
|
dev.send_ds1809_pulse(prt, uc=True, dc=False,
|
||||||
|
count=default_step,
|
||||||
|
pulse_ms=DS1809_INIT_PULSE_MS)
|
||||||
|
current_step = default_step
|
||||||
|
|
||||||
|
return current_step
|
||||||
|
|
||||||
|
|
||||||
def set_initial_params():
|
def set_initial_params():
|
||||||
params = {}
|
params = {}
|
||||||
params['Temp_1'] = INITIAL_TEMPERATURE_1 # Initial temperature for Laser 1
|
params['Temp_1'] = INITIAL_TEMPERATURE_1 # Initial temperature for Laser 1
|
||||||
@ -108,9 +165,14 @@ def set_initial_params():
|
|||||||
params['RampSramMode'] = False
|
params['RampSramMode'] = False
|
||||||
params['RampSramSamples'] = ''
|
params['RampSramSamples'] = ''
|
||||||
params['RampSramAmp'] = ''
|
params['RampSramAmp'] = ''
|
||||||
params['Ad9833Freq'] = ''
|
params['WaveformStatus'] = f'{AD9102_WAVEFORM_FILE_NAME}: не загружен'
|
||||||
params['Ad9833Mclk'] = '25'
|
params['Ad9833Freq'] = str(AD9833_FREQ_DEFAULT_KHZ)
|
||||||
|
params['Ad9833Mclk'] = str(AD9833_MCLK_DEFAULT_MHZ)
|
||||||
params['Ad9833Triangle'] = True
|
params['Ad9833Triangle'] = True
|
||||||
|
params['DS1809Step'] = DS1809_DEFAULT_STEP
|
||||||
|
params['DS1809Status'] = format_ds1809_status(DS1809_DEFAULT_STEP)
|
||||||
|
params['PA4DacVolt'] = str(PA4_DAC_DEFAULT_VOLT)
|
||||||
|
params['PA4DacStatus'] = f"0 / {STM32_DAC_MAX_CODE}"
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def update_data_lists():
|
def update_data_lists():
|
||||||
@ -139,12 +201,29 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# dev.request_state(prt)
|
# dev.request_state(prt)
|
||||||
dev.send_control_parameters(prt, params)
|
dev.send_control_parameters(prt, params)
|
||||||
saved_data.append(dev.request_data(prt))
|
|
||||||
draw_data.append(saved_data[0])
|
|
||||||
|
|
||||||
window = gui.setup_gui(params)
|
window = gui.setup_gui(params)
|
||||||
axes_signs = gui.sign_axes(window)
|
axes_signs = gui.sign_axes(window)
|
||||||
|
|
||||||
|
ds1809_step = initialize_ds1809_position(prt, params['DS1809Step'])
|
||||||
|
params['DS1809Step'] = ds1809_step
|
||||||
|
params['DS1809Status'] = format_ds1809_status(ds1809_step)
|
||||||
|
window['-DS1809Status-'].update(params['DS1809Status'])
|
||||||
|
|
||||||
|
initial_data = None
|
||||||
|
for _ in range(20):
|
||||||
|
initial_data = dev.request_data(prt)
|
||||||
|
if isinstance(initial_data, dict):
|
||||||
|
break
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
if not isinstance(initial_data, dict):
|
||||||
|
print('Error: initial DATA packet not received. Closing program...')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
saved_data.append(initial_data)
|
||||||
|
draw_data.append(initial_data)
|
||||||
|
|
||||||
current_and_temperature_settings_available = True
|
current_and_temperature_settings_available = True
|
||||||
disableStartButton = False
|
disableStartButton = False
|
||||||
|
|
||||||
@ -492,21 +571,56 @@ if __name__ == "__main__":
|
|||||||
sram_mode=sram_mode, sram_samples=sram_samples,
|
sram_mode=sram_mode, sram_samples=sram_samples,
|
||||||
sram_amplitude=sram_amplitude)
|
sram_amplitude=sram_amplitude)
|
||||||
elif event == '-StartRamp9833-':
|
elif event == '-StartRamp9833-':
|
||||||
freq_hz = parse_optional_float(values.get('-AD9833Freq-'))
|
freq_khz = parse_optional_float(values.get('-AD9833Freq-'))
|
||||||
|
freq_hz = int(round(freq_khz * 1000.0)) if freq_khz is not None else None
|
||||||
mclk_mhz = parse_optional_float(values.get('-AD9833Mclk-'))
|
mclk_mhz = parse_optional_float(values.get('-AD9833Mclk-'))
|
||||||
mclk_hz = mclk_mhz * 1e6 if mclk_mhz is not None else None
|
mclk_hz = mclk_mhz * 1e6 if mclk_mhz is not None else None
|
||||||
triangle = values.get('-AD9833Triangle-', True)
|
triangle = values.get('-AD9833Triangle-', True)
|
||||||
dev.start_ad9833_ramp(prt, freq_hz=freq_hz, mclk_hz=mclk_hz, triangle=triangle, enable=True)
|
dev.start_ad9833_ramp(prt, freq_hz=freq_hz, mclk_hz=mclk_hz, triangle=triangle, enable=True)
|
||||||
|
elif event == '-UploadWaveform-':
|
||||||
|
try:
|
||||||
|
waveform = dev.load_ad9102_waveform_file(AD9102_WAVEFORM_PATH)
|
||||||
|
dev.upload_ad9102_waveform(prt, waveform)
|
||||||
|
params['WaveformStatus'] = f'{AD9102_WAVEFORM_FILE_NAME}: {len(waveform)} точек загружено'
|
||||||
|
except Exception as exc:
|
||||||
|
print(f'AD9102 waveform upload failed: {exc}')
|
||||||
|
params['WaveformStatus'] = f'{AD9102_WAVEFORM_FILE_NAME}: ошибка - {exc}'
|
||||||
|
window['-WaveformStatus-'].update(params['WaveformStatus'])
|
||||||
elif event == '-DS1809UC-':
|
elif event == '-DS1809UC-':
|
||||||
dev.send_ds1809_pulse(prt, uc=True, dc=False)
|
dev.send_ds1809_pulse(prt, uc=True, dc=False, count=1, pulse_ms=DS1809_INIT_PULSE_MS)
|
||||||
|
ds1809_step = clamp_int(ds1809_step + 1, 0, DS1809_MAX_STEP)
|
||||||
|
params['DS1809Step'] = ds1809_step
|
||||||
|
params['DS1809Status'] = format_ds1809_status(ds1809_step)
|
||||||
|
window['-DS1809Status-'].update(params['DS1809Status'])
|
||||||
elif event == '-DS1809DC-':
|
elif event == '-DS1809DC-':
|
||||||
dev.send_ds1809_pulse(prt, uc=False, dc=True)
|
dev.send_ds1809_pulse(prt, uc=False, dc=True, count=1, pulse_ms=DS1809_INIT_PULSE_MS)
|
||||||
|
ds1809_step = clamp_int(ds1809_step - 1, 0, DS1809_MAX_STEP)
|
||||||
|
params['DS1809Step'] = ds1809_step
|
||||||
|
params['DS1809Status'] = format_ds1809_status(ds1809_step)
|
||||||
|
window['-DS1809Status-'].update(params['DS1809Status'])
|
||||||
|
elif event == '-SetPA4Dac-':
|
||||||
|
voltage_v = parse_optional_float(values.get('-PA4DacVolt-'))
|
||||||
|
if voltage_v is None:
|
||||||
|
print('Invalid PA4 DAC voltage value.')
|
||||||
|
else:
|
||||||
|
if voltage_v < 0.0:
|
||||||
|
voltage_v = 0.0
|
||||||
|
if voltage_v > STM32_DAC_VREF:
|
||||||
|
voltage_v = STM32_DAC_VREF
|
||||||
|
dac_code = voltage_to_dac_code(voltage_v)
|
||||||
|
dev.set_stm32_dac(prt, dac_code=dac_code, enable=True)
|
||||||
|
params['PA4DacVolt'] = f"{voltage_v:.3f}"
|
||||||
|
params['PA4DacStatus'] = f"{dac_code} / {STM32_DAC_MAX_CODE}"
|
||||||
|
window['-PA4DacVolt-'].update(params['PA4DacVolt'])
|
||||||
|
window['-PA4DacStatus-'].update(params['PA4DacStatus'])
|
||||||
elif event == '-StopCycle-':
|
elif event == '-StopCycle-':
|
||||||
window['-StopCycle-'].update(disabled = True)
|
window['-StopCycle-'].update(disabled = True)
|
||||||
current_and_temperature_settings_available = True
|
current_and_temperature_settings_available = True
|
||||||
stop_task(prt)
|
stop_task(prt)
|
||||||
elif event == TIMEOUT_KEY:
|
elif event == TIMEOUT_KEY:
|
||||||
data = dev.request_data(prt)
|
data = dev.request_data(prt)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
update_data_lists()
|
update_data_lists()
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,12 @@ AD9833_CMD_TOTAL_LENGTH = 10 # Total bytes when sending AD9833 command
|
|||||||
AD9833_CMD_HEADER = "9999"
|
AD9833_CMD_HEADER = "9999"
|
||||||
DS1809_CMD_TOTAL_LENGTH = 10 # Total bytes when sending DS1809 UC/DC pulse command
|
DS1809_CMD_TOTAL_LENGTH = 10 # Total bytes when sending DS1809 UC/DC pulse command
|
||||||
DS1809_CMD_HEADER = "AAAA"
|
DS1809_CMD_HEADER = "AAAA"
|
||||||
|
STM32_DAC_CMD_TOTAL_LENGTH = 10 # Total bytes when sending STM32 DAC command
|
||||||
|
STM32_DAC_CMD_HEADER = "BBBB"
|
||||||
|
AD9102_WAVE_CTRL_TOTAL_LENGTH = 10 # Total bytes when sending AD9102 waveform control command
|
||||||
|
AD9102_WAVE_CTRL_HEADER = "CCCC"
|
||||||
|
AD9102_WAVE_DATA_TOTAL_LENGTH = 30 # Total bytes when sending AD9102 waveform data packet
|
||||||
|
AD9102_WAVE_DATA_HEADER = "DDDD"
|
||||||
AD9102_SAW_STEP_DEFAULT = 1
|
AD9102_SAW_STEP_DEFAULT = 1
|
||||||
AD9102_PAT_PERIOD_DEFAULT = 0xFFFF
|
AD9102_PAT_PERIOD_DEFAULT = 0xFFFF
|
||||||
AD9102_PAT_PERIOD_BASE_DEFAULT = 0x02
|
AD9102_PAT_PERIOD_BASE_DEFAULT = 0x02
|
||||||
@ -25,12 +31,22 @@ AD9102_FLAG_SRAM_FMT = 0x0008
|
|||||||
AD9102_SRAM_SAMPLES_DEFAULT = 16
|
AD9102_SRAM_SAMPLES_DEFAULT = 16
|
||||||
AD9102_SRAM_HOLD_DEFAULT = 1
|
AD9102_SRAM_HOLD_DEFAULT = 1
|
||||||
AD9102_SRAM_AMP_DEFAULT = 8191
|
AD9102_SRAM_AMP_DEFAULT = 8191
|
||||||
|
AD9102_WAVE_OPCODE_BEGIN = 0x0001
|
||||||
|
AD9102_WAVE_OPCODE_COMMIT = 0x0002
|
||||||
|
AD9102_WAVE_OPCODE_CANCEL = 0x0003
|
||||||
|
AD9102_WAVE_CHUNK_SAMPLES = 12
|
||||||
|
AD9102_SAMPLE_MIN = -8192
|
||||||
|
AD9102_SAMPLE_MAX = 8191
|
||||||
|
AD9102_WAVE_MAX_SAMPLES = 4096
|
||||||
AD9833_FLAG_ENABLE = 0x0001
|
AD9833_FLAG_ENABLE = 0x0001
|
||||||
AD9833_FLAG_TRIANGLE = 0x0002
|
AD9833_FLAG_TRIANGLE = 0x0002
|
||||||
AD9833_MCLK_HZ_DEFAULT = 25_000_000
|
AD9833_MCLK_HZ_DEFAULT = 20_000_000
|
||||||
DS1809_FLAG_UC = 0x0001
|
DS1809_FLAG_UC = 0x0001
|
||||||
DS1809_FLAG_DC = 0x0002
|
DS1809_FLAG_DC = 0x0002
|
||||||
DS1809_PULSE_MS_DEFAULT = 2
|
DS1809_PULSE_MS_DEFAULT = 2
|
||||||
|
STM32_DAC_FLAG_ENABLE = 0x0001
|
||||||
|
STM32_DAC_CODE_MAX = 4095
|
||||||
|
STM32_DAC_VREF_DEFAULT = 3.3
|
||||||
|
|
||||||
class TaskType(IntEnum):
|
class TaskType(IntEnum):
|
||||||
Manual = 0x00
|
Manual = 0x00
|
||||||
@ -203,17 +219,56 @@ def send_DS1809(prt, bytestring):
|
|||||||
print("Sent: DS1809 pulse command.")
|
print("Sent: DS1809 pulse command.")
|
||||||
|
|
||||||
|
|
||||||
|
def send_STM32_DAC(prt, bytestring):
|
||||||
|
''' Set STM32 internal DAC output on PA4 (0xBBBB + ...).
|
||||||
|
Expected device answer: STATE.
|
||||||
|
'''
|
||||||
|
if len(bytestring) != STM32_DAC_CMD_TOTAL_LENGTH:
|
||||||
|
print("Error. Wrong parameter string for STM32 DAC command.")
|
||||||
|
return None
|
||||||
|
prt.write(bytestring)
|
||||||
|
print("Sent: STM32 DAC command.")
|
||||||
|
|
||||||
|
|
||||||
|
def send_AD9102_waveform_control(prt, bytestring, quiet: bool = False):
|
||||||
|
''' Control custom AD9102 SRAM upload (0xCCCC + ...).
|
||||||
|
Expected device answer: STATE.
|
||||||
|
'''
|
||||||
|
if len(bytestring) != AD9102_WAVE_CTRL_TOTAL_LENGTH:
|
||||||
|
print("Error. Wrong parameter string for AD9102 waveform control command.")
|
||||||
|
return None
|
||||||
|
prt.write(bytestring)
|
||||||
|
if not quiet:
|
||||||
|
print("Sent: AD9102 waveform control command.")
|
||||||
|
|
||||||
|
|
||||||
|
def send_AD9102_waveform_data(prt, bytestring, quiet: bool = False):
|
||||||
|
''' Send custom AD9102 SRAM samples (0xDDDD + ...).
|
||||||
|
Expected device answer: STATE.
|
||||||
|
'''
|
||||||
|
if len(bytestring) != AD9102_WAVE_DATA_TOTAL_LENGTH:
|
||||||
|
print("Error. Wrong parameter string for AD9102 waveform data command.")
|
||||||
|
return None
|
||||||
|
prt.write(bytestring)
|
||||||
|
if not quiet:
|
||||||
|
print("Sent: AD9102 waveform data command.")
|
||||||
|
|
||||||
|
|
||||||
# ---- Getting data
|
# ---- Getting data
|
||||||
|
|
||||||
|
|
||||||
def get_STATE(prt):
|
def get_STATE(prt, quiet: bool = False):
|
||||||
''' Get decoded state of the device in byte format (2 bytes).
|
''' Get decoded state of the device in byte format (2 bytes).
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
if not quiet:
|
||||||
print("Received "+str(prt.inWaiting())+" bytes.")
|
print("Received "+str(prt.inWaiting())+" bytes.")
|
||||||
if prt.inWaiting()!=2:
|
if prt.inWaiting()!=2:
|
||||||
|
if not quiet:
|
||||||
print("Error. Couldn't get STATE data. prt.inWaiting():", prt.inWaiting())
|
print("Error. Couldn't get STATE data. prt.inWaiting():", prt.inWaiting())
|
||||||
print("Flushing input data:", prt.read(prt.inWaiting()))
|
print("Flushing input data:", prt.read(prt.inWaiting()))
|
||||||
|
else:
|
||||||
|
prt.read(prt.inWaiting())
|
||||||
# print("Flushing input data:", prt.read(2), prt.read(2))
|
# print("Flushing input data:", prt.read(2), prt.read(2))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -222,13 +277,15 @@ def get_STATE(prt):
|
|||||||
|
|
||||||
|
|
||||||
def get_DATA(prt):
|
def get_DATA(prt):
|
||||||
''' Get decoded state of the device in byte format (426 bytes).
|
''' Get decoded state of the device in byte format (30 bytes).
|
||||||
'''
|
'''
|
||||||
|
|
||||||
print("Received "+str(prt.inWaiting())+" bytes.\n")
|
print("Received "+str(prt.inWaiting())+" bytes.\n")
|
||||||
if prt.inWaiting()!=GET_DATA_TOTAL_LENGTH:
|
if prt.inWaiting()!=GET_DATA_TOTAL_LENGTH:
|
||||||
print("Error. Couldn't get DATA data.")
|
print("Error. Couldn't get DATA data.")
|
||||||
print("receiven data len:", prt.inWaiting())
|
print("receiven data len:", prt.inWaiting())
|
||||||
|
if prt.inWaiting() > 0:
|
||||||
|
print("Flushing input data:", prt.read(prt.inWaiting()))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
out_bytes = prt.read(GET_DATA_TOTAL_LENGTH)
|
out_bytes = prt.read(GET_DATA_TOTAL_LENGTH)
|
||||||
@ -494,6 +551,83 @@ def create_DS1809_pulse_command(uc: bool = False,
|
|||||||
return bytearray.fromhex(data)
|
return bytearray.fromhex(data)
|
||||||
|
|
||||||
|
|
||||||
|
def create_STM32_DAC_command(dac_code: int, enable: bool = True):
|
||||||
|
if dac_code is None:
|
||||||
|
dac_code = 0
|
||||||
|
if dac_code < 0:
|
||||||
|
dac_code = 0
|
||||||
|
if dac_code > STM32_DAC_CODE_MAX:
|
||||||
|
dac_code = STM32_DAC_CODE_MAX
|
||||||
|
|
||||||
|
flags = STM32_DAC_FLAG_ENABLE if enable else 0
|
||||||
|
param0 = int(dac_code) & 0x0FFF
|
||||||
|
param1 = 0
|
||||||
|
crc_word = flags ^ param0 ^ param1
|
||||||
|
|
||||||
|
data = flipfour(STM32_DAC_CMD_HEADER) # Word 0 (header)
|
||||||
|
data += flipfour(int_to_hex(flags))
|
||||||
|
data += flipfour(int_to_hex(param0))
|
||||||
|
data += flipfour(int_to_hex(param1))
|
||||||
|
data += flipfour(int_to_hex(crc_word))
|
||||||
|
|
||||||
|
return bytearray.fromhex(data)
|
||||||
|
|
||||||
|
|
||||||
|
def create_AD9102_waveform_control_command(opcode: int, param0: int = 0, param1: int = 0):
|
||||||
|
if opcode is None:
|
||||||
|
opcode = 0
|
||||||
|
if param0 is None:
|
||||||
|
param0 = 0
|
||||||
|
if param1 is None:
|
||||||
|
param1 = 0
|
||||||
|
|
||||||
|
opcode &= 0xFFFF
|
||||||
|
param0 &= 0xFFFF
|
||||||
|
param1 &= 0xFFFF
|
||||||
|
crc_word = opcode ^ param0 ^ param1
|
||||||
|
|
||||||
|
data = flipfour(AD9102_WAVE_CTRL_HEADER) # Word 0 (header)
|
||||||
|
data += flipfour(int_to_hex(opcode))
|
||||||
|
data += flipfour(int_to_hex(param0))
|
||||||
|
data += flipfour(int_to_hex(param1))
|
||||||
|
data += flipfour(int_to_hex(crc_word))
|
||||||
|
|
||||||
|
return bytearray.fromhex(data)
|
||||||
|
|
||||||
|
|
||||||
|
def create_AD9102_waveform_data_command(samples_chunk):
|
||||||
|
if samples_chunk is None:
|
||||||
|
raise ValueError("AD9102 waveform chunk is missing.")
|
||||||
|
|
||||||
|
samples = list(samples_chunk)
|
||||||
|
chunk_count = len(samples)
|
||||||
|
if chunk_count < 1 or chunk_count > AD9102_WAVE_CHUNK_SAMPLES:
|
||||||
|
raise ValueError("AD9102 waveform chunk size must be within [1, 12].")
|
||||||
|
|
||||||
|
encoded_words = [chunk_count]
|
||||||
|
for sample in samples:
|
||||||
|
if isinstance(sample, bool) or not isinstance(sample, int):
|
||||||
|
raise ValueError("AD9102 waveform samples must be integers.")
|
||||||
|
if sample < AD9102_SAMPLE_MIN or sample > AD9102_SAMPLE_MAX:
|
||||||
|
raise ValueError("AD9102 waveform sample is out of range [-8192, 8191].")
|
||||||
|
encoded_words.append(sample & 0xFFFF)
|
||||||
|
|
||||||
|
while len(encoded_words) < (1 + AD9102_WAVE_CHUNK_SAMPLES):
|
||||||
|
encoded_words.append(0)
|
||||||
|
|
||||||
|
crc_word = 0
|
||||||
|
for word in encoded_words:
|
||||||
|
crc_word ^= word
|
||||||
|
|
||||||
|
data = flipfour(AD9102_WAVE_DATA_HEADER) # Word 0 (header)
|
||||||
|
data += flipfour(int_to_hex(encoded_words[0]))
|
||||||
|
for word in encoded_words[1:]:
|
||||||
|
data += flipfour(int_to_hex(word))
|
||||||
|
data += flipfour(int_to_hex(crc_word))
|
||||||
|
|
||||||
|
return bytearray.fromhex(data)
|
||||||
|
|
||||||
|
|
||||||
def encode_Input(params):
|
def encode_Input(params):
|
||||||
|
|
||||||
if params is None:
|
if params is None:
|
||||||
|
|||||||
@ -1,118 +1,178 @@
|
|||||||
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import device_commands as cmd
|
import device_commands as cmd
|
||||||
|
|
||||||
|
|
||||||
#### ---- Constants
|
#### ---- Constants
|
||||||
|
|
||||||
WAIT_AFTER_SEND = 0.15 # Wait after sending command, before requesting input (in seconds).
|
WAIT_AFTER_SEND = 0.15 # Wait after sending command before requesting input (s).
|
||||||
|
FAST_STATE_TIMEOUT_S = 0.4
|
||||||
|
FAST_STATE_POLL_S = 0.005
|
||||||
|
|
||||||
|
|
||||||
#### ---- High-level port commands
|
#### ---- High-level port commands
|
||||||
'''
|
|
||||||
|
def _port_sort_key(port_info):
|
||||||
|
device = str(getattr(port_info, "device", "") or "").lower()
|
||||||
|
description = str(getattr(port_info, "description", "") or "").lower()
|
||||||
|
hwid = str(getattr(port_info, "hwid", "") or "").lower()
|
||||||
|
|
||||||
|
is_usb = ("usb" in device) or ("acm" in device) or ("usb" in description) or ("vid:pid" in hwid)
|
||||||
|
is_builtin_uart = device.startswith("/dev/ttys")
|
||||||
|
|
||||||
|
return (
|
||||||
|
0 if is_usb else 1,
|
||||||
|
1 if is_builtin_uart else 0,
|
||||||
|
device,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_preferred_serial_port(port_info):
|
||||||
|
device = str(getattr(port_info, "device", "") or "").lower()
|
||||||
|
description = str(getattr(port_info, "description", "") or "").lower()
|
||||||
|
hwid = str(getattr(port_info, "hwid", "") or "").lower()
|
||||||
|
return ("usb" in device) or ("acm" in device) or ("usb" in description) or ("vid:pid" in hwid)
|
||||||
|
|
||||||
|
|
||||||
def create_port_connection():
|
def create_port_connection():
|
||||||
prt = None
|
prt = None
|
||||||
for port, _, _ in sorted(cmd.list_ports.comports()):
|
port_infos = list(cmd.list_ports.comports())
|
||||||
try:
|
preferred_ports = [port_info for port_info in port_infos if _is_preferred_serial_port(port_info)]
|
||||||
prt = cmd.setup_port_connection(port=port, baudrate=115200, timeout_sec=1)
|
if preferred_ports:
|
||||||
cmd.open_port(prt)
|
port_infos = preferred_ports
|
||||||
reset_port_settings(prt)
|
|
||||||
except:
|
for port_info in sorted(port_infos, key=_port_sort_key):
|
||||||
prt.close()
|
port = getattr(port_info, "device", None)
|
||||||
|
if not port:
|
||||||
continue
|
continue
|
||||||
break
|
|
||||||
return prt
|
|
||||||
'''
|
|
||||||
def create_port_connection():
|
|
||||||
prt = None
|
|
||||||
print()
|
|
||||||
ports = []
|
|
||||||
for port, _,_ in sorted(cmd.list_ports.comports()):
|
|
||||||
ports.append(port)
|
|
||||||
|
|
||||||
#ONLY FOR LINUX!!!
|
|
||||||
have_ttyUSB = False
|
|
||||||
USB_ports = []
|
|
||||||
for port in ports:
|
|
||||||
if "USB" in port:
|
|
||||||
USB_ports.append(port)
|
|
||||||
if len(USB_ports):
|
|
||||||
ports = USB_ports
|
|
||||||
# print("ports:", ports)
|
|
||||||
|
|
||||||
|
|
||||||
# for port, _, _ in sorted(cmd.list_ports.comports()):
|
|
||||||
for port in ports:
|
|
||||||
try:
|
try:
|
||||||
print("PORT:", port)
|
|
||||||
prt = cmd.setup_port_connection(port=port, baudrate=115200, timeout_sec=1)
|
prt = cmd.setup_port_connection(port=port, baudrate=115200, timeout_sec=1)
|
||||||
cmd.open_port(prt)
|
cmd.open_port(prt)
|
||||||
reset_port_settings(prt)
|
if not reset_port_settings(prt):
|
||||||
except:
|
raise RuntimeError(f"No valid STATE reply on {port}")
|
||||||
|
except Exception:
|
||||||
|
if prt is not None and prt.is_open:
|
||||||
prt.close()
|
prt.close()
|
||||||
|
prt = None
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
return prt
|
return prt
|
||||||
|
|
||||||
|
|
||||||
|
def _print_state_reply(state_bytes):
|
||||||
|
if state_bytes is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
status = state_bytes.hex()
|
||||||
|
print("Received: STATE. State status:", cmd.decode_STATE(status), "(" + cmd.flipfour(status) + ")")
|
||||||
|
print("")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# def setup_connection():
|
def _decode_state_word(state_bytes):
|
||||||
# prt = cmd.setup_port_connection()
|
if state_bytes is None:
|
||||||
# cmd.open_port(prt)
|
return None
|
||||||
# return prt
|
try:
|
||||||
|
return int(cmd.flipfour(state_bytes.hex()), 16)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _state_has_errors(state_bytes):
|
||||||
|
state_word = _decode_state_word(state_bytes)
|
||||||
|
if state_word is None:
|
||||||
|
return True
|
||||||
|
return (state_word & 0x00FF) != 0
|
||||||
|
|
||||||
|
|
||||||
|
def _state_message(state_bytes):
|
||||||
|
if state_bytes is None:
|
||||||
|
return "STATE reply not received."
|
||||||
|
return cmd.decode_STATE(state_bytes.hex())
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_state_reply(prt, timeout_s=WAIT_AFTER_SEND, poll_s=0.01, quiet=False):
|
||||||
|
if not _wait_for_min_bytes(prt, expected_len=2, timeout_s=timeout_s, poll_s=poll_s):
|
||||||
|
if not quiet:
|
||||||
|
print("Error. Timed out waiting for STATE.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
state_bytes = cmd.get_STATE(prt, quiet=quiet)
|
||||||
|
if quiet:
|
||||||
|
return state_bytes
|
||||||
|
_print_state_reply(state_bytes)
|
||||||
|
return state_bytes
|
||||||
|
|
||||||
|
|
||||||
|
def _rollback_ad9102_waveform_upload(prt):
|
||||||
|
try:
|
||||||
|
cancel_cmd = cmd.create_AD9102_waveform_control_command(cmd.AD9102_WAVE_OPCODE_CANCEL)
|
||||||
|
cmd.send_AD9102_waveform_control(prt, cancel_cmd, quiet=True)
|
||||||
|
state_bytes = _wait_for_state_reply(prt, timeout_s=FAST_STATE_TIMEOUT_S,
|
||||||
|
poll_s=FAST_STATE_POLL_S, quiet=True)
|
||||||
|
if state_bytes is not None and not _state_has_errors(state_bytes):
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
reset_port_settings(prt)
|
||||||
|
|
||||||
|
|
||||||
def reset_port_settings(prt):
|
def reset_port_settings(prt):
|
||||||
# Reset port settings and check status
|
# Reset port settings and check status
|
||||||
|
try:
|
||||||
|
prt.reset_input_buffer()
|
||||||
|
prt.reset_output_buffer()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
cmd.send_DEFAULT_ENABLE(prt)
|
cmd.send_DEFAULT_ENABLE(prt)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
state_bytes = _wait_for_state_reply(prt, timeout_s=max(WAIT_AFTER_SEND, 0.4), poll_s=0.01, quiet=True)
|
||||||
status = cmd.get_STATE(prt).hex()
|
if state_bytes is not None:
|
||||||
if status is not None:
|
status = state_bytes.hex()
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "(" + cmd.flipfour(status) + ")")
|
print("Received: STATE. State status:", cmd.decode_STATE(status), "(" + cmd.flipfour(status) + ")")
|
||||||
print("")
|
print("")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def request_state(prt):
|
def request_state(prt):
|
||||||
# Request data
|
|
||||||
cmd.send_STATE(prt)
|
cmd.send_STATE(prt)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
status = cmd.get_STATE(prt).hex()
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
if status is not None:
|
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
|
|
||||||
def send_control_parameters(prt, params):
|
def send_control_parameters(prt, params):
|
||||||
# Send control parameters
|
|
||||||
hexstring = cmd.encode_Input(params)
|
hexstring = cmd.encode_Input(params)
|
||||||
cmd.send_DECODE_ENABLE(prt, hexstring)
|
cmd.send_DECODE_ENABLE(prt, hexstring)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
status = cmd.get_STATE(prt).hex()
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
if status is not None:
|
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
|
|
||||||
print("")
|
|
||||||
else:
|
|
||||||
print("")
|
|
||||||
|
|
||||||
def send_task_command(prt, sending_param):
|
def send_task_command(prt, sending_param):
|
||||||
# Send task command (TASK_ENABLE state in firmware)
|
# Send task command (TASK_ENABLE state in firmware)
|
||||||
hexstring = cmd.create_TaskEnableCommand(sending_param)
|
hexstring = cmd.create_TaskEnableCommand(sending_param)
|
||||||
cmd.send_TASK_ENABLE(prt, hexstring)
|
cmd.send_TASK_ENABLE(prt, hexstring)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
status = cmd.get_STATE(prt).hex()
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
if status is not None:
|
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
|
|
||||||
print("")
|
|
||||||
else:
|
|
||||||
print("")
|
|
||||||
|
|
||||||
|
|
||||||
|
def start_ramp_max(
|
||||||
def start_ramp_max(prt, freq_hz=None, duty=None, saw_step=None, pat_period=None, pat_period_base=None, dac_clk_hz=None, triangle=True, sram_mode=False, sram_samples=None, sram_hold=None, sram_amplitude=None):
|
prt,
|
||||||
|
freq_hz=None,
|
||||||
|
duty=None,
|
||||||
|
saw_step=None,
|
||||||
|
pat_period=None,
|
||||||
|
pat_period_base=None,
|
||||||
|
dac_clk_hz=None,
|
||||||
|
triangle=True,
|
||||||
|
sram_mode=False,
|
||||||
|
sram_samples=None,
|
||||||
|
sram_hold=None,
|
||||||
|
sram_amplitude=None,
|
||||||
|
):
|
||||||
# Start AD9102 sawtooth with configurable frequency/duty or SRAM ramp mode
|
# Start AD9102 sawtooth with configurable frequency/duty or SRAM ramp mode
|
||||||
if sram_mode:
|
if sram_mode:
|
||||||
if sram_hold is None:
|
if sram_hold is None:
|
||||||
@ -121,9 +181,14 @@ def start_ramp_max(prt, freq_hz=None, duty=None, saw_step=None, pat_period=None,
|
|||||||
if dac_clk_hz is None:
|
if dac_clk_hz is None:
|
||||||
dac_clk_hz = cmd.AD9102_DAC_CLK_HZ
|
dac_clk_hz = cmd.AD9102_DAC_CLK_HZ
|
||||||
sram_samples = cmd.calc_sram_samples_for_freq(freq_hz, dac_clk_hz, sram_hold)
|
sram_samples = cmd.calc_sram_samples_for_freq(freq_hz, dac_clk_hz, sram_hold)
|
||||||
hexstring = cmd.create_AD9102_ramp_command(enable=True, triangle=triangle, sram_mode=True,
|
hexstring = cmd.create_AD9102_ramp_command(
|
||||||
sram_samples=sram_samples, sram_hold=sram_hold,
|
enable=True,
|
||||||
sram_amplitude=sram_amplitude)
|
triangle=triangle,
|
||||||
|
sram_mode=True,
|
||||||
|
sram_samples=sram_samples,
|
||||||
|
sram_hold=sram_hold,
|
||||||
|
sram_amplitude=sram_amplitude,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if pat_period_base is None:
|
if pat_period_base is None:
|
||||||
pat_period_base = cmd.AD9102_PAT_PERIOD_BASE_DEFAULT
|
pat_period_base = cmd.AD9102_PAT_PERIOD_BASE_DEFAULT
|
||||||
@ -137,54 +202,148 @@ def start_ramp_max(prt, freq_hz=None, duty=None, saw_step=None, pat_period=None,
|
|||||||
pat_period = cmd.calc_pat_period_for_duty(saw_step, duty, pat_period_base, triangle)
|
pat_period = cmd.calc_pat_period_for_duty(saw_step, duty, pat_period_base, triangle)
|
||||||
if pat_period is None:
|
if pat_period is None:
|
||||||
pat_period = cmd.AD9102_PAT_PERIOD_DEFAULT
|
pat_period = cmd.AD9102_PAT_PERIOD_DEFAULT
|
||||||
hexstring = cmd.create_AD9102_ramp_command(saw_step, pat_period, pat_period_base,
|
hexstring = cmd.create_AD9102_ramp_command(
|
||||||
enable=True, triangle=triangle)
|
saw_step,
|
||||||
|
pat_period,
|
||||||
|
pat_period_base,
|
||||||
|
enable=True,
|
||||||
|
triangle=triangle,
|
||||||
|
)
|
||||||
|
|
||||||
cmd.send_AD9102(prt, hexstring)
|
cmd.send_AD9102(prt, hexstring)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
status = cmd.get_STATE(prt).hex()
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
if status is not None:
|
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
|
|
||||||
print("")
|
|
||||||
else:
|
|
||||||
print("")
|
|
||||||
|
|
||||||
|
|
||||||
def start_ad9833_ramp(prt, freq_hz=None, mclk_hz=None, triangle=True, enable=True):
|
def start_ad9833_ramp(prt, freq_hz=None, mclk_hz=None, triangle=True, enable=True):
|
||||||
if freq_hz is None:
|
if freq_hz is None:
|
||||||
freq_hz = 0.0
|
freq_hz = 0.0
|
||||||
hexstring = cmd.create_AD9833_ramp_command(freq_hz=freq_hz, mclk_hz=mclk_hz,
|
hexstring = cmd.create_AD9833_ramp_command(
|
||||||
enable=enable, triangle=triangle)
|
freq_hz=freq_hz,
|
||||||
|
mclk_hz=mclk_hz,
|
||||||
|
enable=enable,
|
||||||
|
triangle=triangle,
|
||||||
|
)
|
||||||
cmd.send_AD9833(prt, hexstring)
|
cmd.send_AD9833(prt, hexstring)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
status = cmd.get_STATE(prt).hex()
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
if status is not None:
|
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
|
|
||||||
print("")
|
def set_stm32_dac(prt, dac_code, enable=True):
|
||||||
else:
|
hexstring = cmd.create_STM32_DAC_command(dac_code=dac_code, enable=enable)
|
||||||
print("")
|
cmd.send_STM32_DAC(prt, hexstring)
|
||||||
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
|
|
||||||
|
|
||||||
|
def load_ad9102_waveform_file(filepath):
|
||||||
|
try:
|
||||||
|
with open(filepath, "r", encoding="utf-8") as fh:
|
||||||
|
payload = json.load(fh)
|
||||||
|
except FileNotFoundError as exc:
|
||||||
|
raise ValueError(f"Waveform file not found: {filepath}") from exc
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise ValueError(f"Invalid JSON in waveform file: {exc.msg}") from exc
|
||||||
|
|
||||||
|
if not isinstance(payload, list):
|
||||||
|
raise ValueError("Waveform file must contain a JSON array.")
|
||||||
|
|
||||||
|
if len(payload) < 2 or len(payload) > cmd.AD9102_WAVE_MAX_SAMPLES:
|
||||||
|
raise ValueError(f"Waveform length must be within [2, {cmd.AD9102_WAVE_MAX_SAMPLES}].")
|
||||||
|
|
||||||
|
samples = []
|
||||||
|
for index, sample in enumerate(payload):
|
||||||
|
if isinstance(sample, bool) or not isinstance(sample, int):
|
||||||
|
raise ValueError(f"Waveform sample #{index} is not an integer.")
|
||||||
|
if sample < cmd.AD9102_SAMPLE_MIN or sample > cmd.AD9102_SAMPLE_MAX:
|
||||||
|
raise ValueError(
|
||||||
|
f"Waveform sample #{index} is out of range "
|
||||||
|
f"[{cmd.AD9102_SAMPLE_MIN}, {cmd.AD9102_SAMPLE_MAX}]."
|
||||||
|
)
|
||||||
|
samples.append(int(sample))
|
||||||
|
|
||||||
|
return samples
|
||||||
|
|
||||||
|
|
||||||
|
def upload_ad9102_waveform(prt, samples):
|
||||||
|
waveform = list(samples) if samples is not None else []
|
||||||
|
if len(waveform) < 2 or len(waveform) > cmd.AD9102_WAVE_MAX_SAMPLES:
|
||||||
|
raise ValueError(f"Waveform length must be within [2, {cmd.AD9102_WAVE_MAX_SAMPLES}].")
|
||||||
|
|
||||||
|
try:
|
||||||
|
begin_cmd = cmd.create_AD9102_waveform_control_command(
|
||||||
|
cmd.AD9102_WAVE_OPCODE_BEGIN,
|
||||||
|
param0=len(waveform),
|
||||||
|
param1=0,
|
||||||
|
)
|
||||||
|
cmd.send_AD9102_waveform_control(prt, begin_cmd, quiet=True)
|
||||||
|
state_bytes = _wait_for_state_reply(prt, timeout_s=FAST_STATE_TIMEOUT_S,
|
||||||
|
poll_s=FAST_STATE_POLL_S, quiet=True)
|
||||||
|
if state_bytes is None or _state_has_errors(state_bytes):
|
||||||
|
raise RuntimeError(f"Waveform BEGIN failed: {_state_message(state_bytes)}")
|
||||||
|
|
||||||
|
for offset in range(0, len(waveform), cmd.AD9102_WAVE_CHUNK_SAMPLES):
|
||||||
|
chunk = waveform[offset:offset + cmd.AD9102_WAVE_CHUNK_SAMPLES]
|
||||||
|
chunk_cmd = cmd.create_AD9102_waveform_data_command(chunk)
|
||||||
|
cmd.send_AD9102_waveform_data(prt, chunk_cmd, quiet=True)
|
||||||
|
state_bytes = _wait_for_state_reply(prt, timeout_s=FAST_STATE_TIMEOUT_S,
|
||||||
|
poll_s=FAST_STATE_POLL_S, quiet=True)
|
||||||
|
if state_bytes is None or _state_has_errors(state_bytes):
|
||||||
|
chunk_no = (offset // cmd.AD9102_WAVE_CHUNK_SAMPLES) + 1
|
||||||
|
raise RuntimeError(f"Waveform DATA chunk {chunk_no} failed: {_state_message(state_bytes)}")
|
||||||
|
|
||||||
|
commit_cmd = cmd.create_AD9102_waveform_control_command(cmd.AD9102_WAVE_OPCODE_COMMIT)
|
||||||
|
cmd.send_AD9102_waveform_control(prt, commit_cmd, quiet=True)
|
||||||
|
state_bytes = _wait_for_state_reply(prt, timeout_s=FAST_STATE_TIMEOUT_S,
|
||||||
|
poll_s=FAST_STATE_POLL_S, quiet=True)
|
||||||
|
if state_bytes is None or _state_has_errors(state_bytes):
|
||||||
|
raise RuntimeError(f"Waveform COMMIT failed: {_state_message(state_bytes)}")
|
||||||
|
|
||||||
|
print(f"Uploaded AD9102 waveform ({len(waveform)} samples).")
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
_rollback_ad9102_waveform_upload(prt)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_min_bytes(prt, expected_len, timeout_s, poll_s=0.01):
|
||||||
|
deadline = time.time() + timeout_s
|
||||||
|
while time.time() < deadline:
|
||||||
|
waiting = prt.inWaiting()
|
||||||
|
if waiting >= expected_len:
|
||||||
|
return True
|
||||||
|
time.sleep(poll_s)
|
||||||
|
return prt.inWaiting() >= expected_len
|
||||||
|
|
||||||
|
|
||||||
def send_ds1809_pulse(prt, uc=False, dc=False, count=1, pulse_ms=None):
|
def send_ds1809_pulse(prt, uc=False, dc=False, count=1, pulse_ms=None):
|
||||||
|
if count is None or count <= 0:
|
||||||
|
count = 1
|
||||||
|
if pulse_ms is None or pulse_ms <= 0:
|
||||||
|
pulse_ms = cmd.DS1809_PULSE_MS_DEFAULT
|
||||||
|
|
||||||
hexstring = cmd.create_DS1809_pulse_command(uc=uc, dc=dc, count=count, pulse_ms=pulse_ms)
|
hexstring = cmd.create_DS1809_pulse_command(uc=uc, dc=dc, count=count, pulse_ms=pulse_ms)
|
||||||
cmd.send_DS1809(prt, hexstring)
|
cmd.send_DS1809(prt, hexstring)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
|
||||||
status = cmd.get_STATE(prt).hex()
|
# Firmware blocks while pulsing DS1809 lines: wait pulse train + safe margin.
|
||||||
if status is not None:
|
pulse_train_time = (2.0 * float(count) * float(pulse_ms)) / 1000.0
|
||||||
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
|
time.sleep(max(WAIT_AFTER_SEND, pulse_train_time + 0.35))
|
||||||
print("")
|
|
||||||
else:
|
# Then poll shortly for STATE bytes; this avoids early read (0 bytes) on startup.
|
||||||
print("")
|
_wait_for_min_bytes(prt, expected_len=2, timeout_s=0.8)
|
||||||
|
|
||||||
|
return _print_state_reply(cmd.get_STATE(prt))
|
||||||
|
|
||||||
|
|
||||||
def request_data(prt):
|
def request_data(prt):
|
||||||
# Request data
|
|
||||||
cmd.send_TRANS_ENABLE(prt)
|
cmd.send_TRANS_ENABLE(prt)
|
||||||
time.sleep(WAIT_AFTER_SEND)
|
time.sleep(WAIT_AFTER_SEND)
|
||||||
data = cmd.get_DATA(prt).hex()
|
|
||||||
data_dict = []
|
data_bytes = cmd.get_DATA(prt)
|
||||||
if data is not None:
|
if data_bytes is None:
|
||||||
data_dict = cmd.decode_DATA(data)
|
return None
|
||||||
return data_dict
|
|
||||||
|
return cmd.decode_DATA(data_bytes.hex())
|
||||||
|
|
||||||
|
|
||||||
def print_data(data):
|
def print_data(data):
|
||||||
@ -192,19 +351,41 @@ def print_data(data):
|
|||||||
return str(round(i, 2))
|
return str(round(i, 2))
|
||||||
|
|
||||||
print("Data from device (time: " + datetime.now().strftime("%H:%M:%S:%f") + "):")
|
print("Data from device (time: " + datetime.now().strftime("%H:%M:%S:%f") + "):")
|
||||||
print("Message Header:", data['Header'], " Message ID:", data['Message_ID'])
|
print("Message Header:", data["Header"], " Message ID:", data["Message_ID"])
|
||||||
print("Photodiode Current 1 ("+str(len(data['I1']))+" values):", \
|
print(
|
||||||
shorten(data['I1']), shorten(data['I1'][1]), "...", \
|
"Photodiode Current 1 (" + str(len(data["I1"])) + " values):",
|
||||||
shorten(data['I1']), shorten(data['I1'][-1]), "mA")
|
shorten(data["I1"]),
|
||||||
print("Photodiode Current 2 ("+str(len(data['I2']))+" values):", \
|
shorten(data["I1"][1]),
|
||||||
shorten(data['I2']), shorten(data['I2'][1]), "...", \
|
"...",
|
||||||
shorten(data['I2']), shorten(data['I2'][-1]), "mA")
|
shorten(data["I1"]),
|
||||||
print("Laser Temperature 1:", shorten(data['Temp_1']), "C")
|
shorten(data["I1"][-1]),
|
||||||
print("Laser Temperature 2:", shorten(data['Temp_2']), "C")
|
"mA",
|
||||||
print("Temperature of external thermistor 1:", shorten(data['Temp_Ext_1']), "C")
|
)
|
||||||
print("Temperature of external thermistor 2:", shorten(data['Temp_Ext_2']), "C")
|
print(
|
||||||
print("Voltages 3V3: "+shorten(data['MON_3V3'])+"V 5V1: "+shorten(data['MON_5V1'])+ \
|
"Photodiode Current 2 (" + str(len(data["I2"])) + " values):",
|
||||||
"V 5V2: "+shorten(data['MON_5V2'])+"V 7V0: "+shorten(data['MON_7V0'])+"V.")
|
shorten(data["I2"]),
|
||||||
|
shorten(data["I2"][1]),
|
||||||
|
"...",
|
||||||
|
shorten(data["I2"]),
|
||||||
|
shorten(data["I2"][-1]),
|
||||||
|
"mA",
|
||||||
|
)
|
||||||
|
print("Laser Temperature 1:", shorten(data["Temp_1"]), "C")
|
||||||
|
print("Laser Temperature 2:", shorten(data["Temp_2"]), "C")
|
||||||
|
print("Temperature of external thermistor 1:", shorten(data["Temp_Ext_1"]), "C")
|
||||||
|
print("Temperature of external thermistor 2:", shorten(data["Temp_Ext_2"]), "C")
|
||||||
|
print(
|
||||||
|
"Voltages 3V3: "
|
||||||
|
+ shorten(data["MON_3V3"])
|
||||||
|
+ "V 5V1: "
|
||||||
|
+ shorten(data["MON_5V1"])
|
||||||
|
+ "V 5V2: "
|
||||||
|
+ shorten(data["MON_5V2"])
|
||||||
|
+ "V 7V0: "
|
||||||
|
+ shorten(data["MON_7V0"])
|
||||||
|
+ "V."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def close_connection(prt):
|
def close_connection(prt):
|
||||||
cmd.close_port(prt)
|
cmd.close_port(prt)
|
||||||
|
|||||||
30
gui.py
30
gui.py
@ -50,14 +50,21 @@ SET_RAMP_TRI_TEXT = 'Треугольник'
|
|||||||
SET_RAMP_SRAM_MODE_TEXT = 'SRAM режим'
|
SET_RAMP_SRAM_MODE_TEXT = 'SRAM режим'
|
||||||
SET_RAMP_SRAM_SAMPLES_TEXT = 'SRAM точки (samples):'
|
SET_RAMP_SRAM_SAMPLES_TEXT = 'SRAM точки (samples):'
|
||||||
SET_RAMP_SRAM_AMP_TEXT = 'SRAM амплитуда (%):'
|
SET_RAMP_SRAM_AMP_TEXT = 'SRAM амплитуда (%):'
|
||||||
|
SET_WAVEFORM_BUTTON_TEXT = 'Загрузить форму'
|
||||||
|
SET_WAVEFORM_STATUS_TEXT = 'Форма AD9102:'
|
||||||
SET_AD9833_SECTION_TEXT = 'Настройка пилы (AD9833)'
|
SET_AD9833_SECTION_TEXT = 'Настройка пилы (AD9833)'
|
||||||
SET_AD9833_FREQ_TEXT = 'Частота AD9833 (Гц):'
|
SET_AD9833_FREQ_TEXT = 'Частота AD9833 (кГц):'
|
||||||
SET_AD9833_MCLK_TEXT = 'MCLK AD9833 (МГц):'
|
SET_AD9833_MCLK_TEXT = 'MCLK AD9833 (МГц):'
|
||||||
SET_AD9833_TRI_TEXT = 'Треугольник AD9833'
|
SET_AD9833_TRI_TEXT = 'Треугольник AD9833'
|
||||||
SET_AD9833_BUTTON_TEXT = 'Пила AD9833'
|
SET_AD9833_BUTTON_TEXT = 'Пила AD9833'
|
||||||
SET_DS1809_SECTION_TEXT = 'DS1809 (UC/DC)'
|
SET_DS1809_SECTION_TEXT = 'DS1809 (UC/DC)'
|
||||||
SET_DS1809_UC_BUTTON_TEXT = 'UC импульс'
|
SET_DS1809_UC_BUTTON_TEXT = 'UC импульс'
|
||||||
SET_DS1809_DC_BUTTON_TEXT = 'DC импульс'
|
SET_DS1809_DC_BUTTON_TEXT = 'DC импульс'
|
||||||
|
SET_DS1809_STATUS_TEXT = 'Позиция DS1809:'
|
||||||
|
SET_STM32_DAC_SECTION_TEXT = 'STM32 DAC (PA4)'
|
||||||
|
SET_STM32_DAC_VOLT_TEXT = 'Напряжение DAC PA4 (В, 0..2.5):'
|
||||||
|
SET_STM32_DAC_BUTTON_TEXT = 'Установить DAC'
|
||||||
|
SET_STM32_DAC_STATUS_TEXT = 'Код DAC PA4:'
|
||||||
|
|
||||||
|
|
||||||
GRAPH_POINTS_NUMBER = 100 # Number of most recent data points shown on charts
|
GRAPH_POINTS_NUMBER = 100 # Number of most recent data points shown on charts
|
||||||
@ -270,6 +277,12 @@ def setup_gui(params):
|
|||||||
[sg.Text(SET_RAMP_SRAM_AMP_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
[sg.Text(SET_RAMP_SRAM_AMP_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
sg.Input(params.get('RampSramAmp', ''), size=(SET_INPUT_WIDTH,1), key='-RampSramAmp-')],
|
sg.Input(params.get('RampSramAmp', ''), size=(SET_INPUT_WIDTH,1), key='-RampSramAmp-')],
|
||||||
|
|
||||||
|
[sg.Button(SET_WAVEFORM_BUTTON_TEXT, key='-UploadWaveform-', disabled_button_color=("Gray22", "Blue"))],
|
||||||
|
|
||||||
|
[sg.Text(SET_WAVEFORM_STATUS_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Text(params.get('WaveformStatus', 'waveform.json: не загружен'),
|
||||||
|
key='-WaveformStatus-', size=(30,2))],
|
||||||
|
|
||||||
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
|
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
|
||||||
|
|
||||||
[sg.Text(SET_AD9833_SECTION_TEXT, size=(SET_TEXT_WIDTH_NEW,1))],
|
[sg.Text(SET_AD9833_SECTION_TEXT, size=(SET_TEXT_WIDTH_NEW,1))],
|
||||||
@ -290,6 +303,21 @@ def setup_gui(params):
|
|||||||
[sg.Button(SET_DS1809_UC_BUTTON_TEXT, key='-DS1809UC-', disabled_button_color=("Gray22", "Blue")),
|
[sg.Button(SET_DS1809_UC_BUTTON_TEXT, key='-DS1809UC-', disabled_button_color=("Gray22", "Blue")),
|
||||||
sg.Button(SET_DS1809_DC_BUTTON_TEXT, key='-DS1809DC-', disabled_button_color=("Gray22", "Blue"))],
|
sg.Button(SET_DS1809_DC_BUTTON_TEXT, key='-DS1809DC-', disabled_button_color=("Gray22", "Blue"))],
|
||||||
|
|
||||||
|
[sg.Text(SET_DS1809_STATUS_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Text(params.get('DS1809Status', '--'), key='-DS1809Status-', size=(20,1))],
|
||||||
|
|
||||||
|
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
|
||||||
|
|
||||||
|
[sg.Text(SET_STM32_DAC_SECTION_TEXT, size=(SET_TEXT_WIDTH_NEW,1))],
|
||||||
|
|
||||||
|
[sg.Text(SET_STM32_DAC_VOLT_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('PA4DacVolt', '0.0'), size=(SET_INPUT_WIDTH,1), key='-PA4DacVolt-')],
|
||||||
|
|
||||||
|
[sg.Button(SET_STM32_DAC_BUTTON_TEXT, key='-SetPA4Dac-', disabled_button_color=("Gray22", "Blue"))],
|
||||||
|
|
||||||
|
[sg.Text(SET_STM32_DAC_STATUS_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Text(params.get('PA4DacStatus', '0 / 4095'), key='-PA4DacStatus-', size=(20,1))],
|
||||||
|
|
||||||
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
|
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
|
||||||
|
|
||||||
[sg.Button(SET_START_BUTTON_TEXT, key='-StartCycle-', disabled_button_color=("Gray22", "Blue"), disabled=True), sg.Button(SET_STOP_BUTTON_TEXT, disabled_button_color=("Gray22", "Blue"), key='-StopCycle-', disabled=True), sg.Button(SET_RAMP_BUTTON_TEXT, key='-StartRamp-', disabled_button_color=("Gray22", "Blue")), sg.Button(SET_AD9833_BUTTON_TEXT, key='-StartRamp9833-', disabled_button_color=("Gray22", "Blue"))]]
|
[sg.Button(SET_START_BUTTON_TEXT, key='-StartCycle-', disabled_button_color=("Gray22", "Blue"), disabled=True), sg.Button(SET_STOP_BUTTON_TEXT, disabled_button_color=("Gray22", "Blue"), key='-StopCycle-', disabled=True), sg.Button(SET_RAMP_BUTTON_TEXT, key='-StartRamp-', disabled_button_color=("Gray22", "Blue")), sg.Button(SET_AD9833_BUTTON_TEXT, key='-StartRamp9833-', disabled_button_color=("Gray22", "Blue"))]]
|
||||||
|
|||||||
911
waveform.json
Normal file
911
waveform.json
Normal file
@ -0,0 +1,911 @@
|
|||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
96,
|
||||||
|
863,
|
||||||
|
1488,
|
||||||
|
1920,
|
||||||
|
2149,
|
||||||
|
2210,
|
||||||
|
2170,
|
||||||
|
2119,
|
||||||
|
2145,
|
||||||
|
2319,
|
||||||
|
2676,
|
||||||
|
3211,
|
||||||
|
3874,
|
||||||
|
4586,
|
||||||
|
5253,
|
||||||
|
5787,
|
||||||
|
6121,
|
||||||
|
6229,
|
||||||
|
6126,
|
||||||
|
5867,
|
||||||
|
5532,
|
||||||
|
5214,
|
||||||
|
4993,
|
||||||
|
4921,
|
||||||
|
5013,
|
||||||
|
5239,
|
||||||
|
5536,
|
||||||
|
5818,
|
||||||
|
5994,
|
||||||
|
5991,
|
||||||
|
5767,
|
||||||
|
5320,
|
||||||
|
4689,
|
||||||
|
3946,
|
||||||
|
3180,
|
||||||
|
2478,
|
||||||
|
1907,
|
||||||
|
1501,
|
||||||
|
1254,
|
||||||
|
1122,
|
||||||
|
1035,
|
||||||
|
909,
|
||||||
|
671,
|
||||||
|
269,
|
||||||
|
314,
|
||||||
|
1052,
|
||||||
|
1888,
|
||||||
|
2740,
|
||||||
|
3521,
|
||||||
|
4155,
|
||||||
|
4593,
|
||||||
|
4821,
|
||||||
|
4866,
|
||||||
|
4781,
|
||||||
|
4642,
|
||||||
|
4523,
|
||||||
|
4486,
|
||||||
|
4562,
|
||||||
|
4749,
|
||||||
|
5012,
|
||||||
|
5287,
|
||||||
|
5502,
|
||||||
|
5585,
|
||||||
|
5487,
|
||||||
|
5187,
|
||||||
|
4702,
|
||||||
|
4077,
|
||||||
|
3383,
|
||||||
|
2698,
|
||||||
|
2093,
|
||||||
|
1618,
|
||||||
|
1293,
|
||||||
|
1106,
|
||||||
|
1016,
|
||||||
|
965,
|
||||||
|
890,
|
||||||
|
739,
|
||||||
|
481,
|
||||||
|
115,
|
||||||
|
330,
|
||||||
|
805,
|
||||||
|
1246,
|
||||||
|
1591,
|
||||||
|
1793,
|
||||||
|
1831,
|
||||||
|
1711,
|
||||||
|
1470,
|
||||||
|
1164,
|
||||||
|
862,
|
||||||
|
625,
|
||||||
|
502,
|
||||||
|
516,
|
||||||
|
663,
|
||||||
|
914,
|
||||||
|
1221,
|
||||||
|
1530,
|
||||||
|
1794,
|
||||||
|
1981,
|
||||||
|
2080,
|
||||||
|
2106,
|
||||||
|
2094,
|
||||||
|
2091,
|
||||||
|
2145,
|
||||||
|
2293,
|
||||||
|
2554,
|
||||||
|
2920,
|
||||||
|
3362,
|
||||||
|
3830,
|
||||||
|
4264,
|
||||||
|
4605,
|
||||||
|
4808,
|
||||||
|
4846,
|
||||||
|
4719,
|
||||||
|
4449,
|
||||||
|
4078,
|
||||||
|
3656,
|
||||||
|
3235,
|
||||||
|
2855,
|
||||||
|
2536,
|
||||||
|
2280,
|
||||||
|
2069,
|
||||||
|
1872,
|
||||||
|
1654,
|
||||||
|
1385,
|
||||||
|
1046,
|
||||||
|
640,
|
||||||
|
188,
|
||||||
|
271,
|
||||||
|
691,
|
||||||
|
1024,
|
||||||
|
1234,
|
||||||
|
1302,
|
||||||
|
1231,
|
||||||
|
1051,
|
||||||
|
807,
|
||||||
|
556,
|
||||||
|
357,
|
||||||
|
256,
|
||||||
|
283,
|
||||||
|
444,
|
||||||
|
723,
|
||||||
|
1084,
|
||||||
|
1480,
|
||||||
|
1863,
|
||||||
|
2191,
|
||||||
|
2440,
|
||||||
|
2602,
|
||||||
|
2690,
|
||||||
|
2729,
|
||||||
|
2749,
|
||||||
|
2779,
|
||||||
|
2835,
|
||||||
|
2917,
|
||||||
|
3006,
|
||||||
|
3069,
|
||||||
|
3065,
|
||||||
|
2954,
|
||||||
|
2708,
|
||||||
|
2318,
|
||||||
|
1801,
|
||||||
|
1198,
|
||||||
|
568,
|
||||||
|
18,
|
||||||
|
494,
|
||||||
|
805,
|
||||||
|
923,
|
||||||
|
850,
|
||||||
|
616,
|
||||||
|
278,
|
||||||
|
91,
|
||||||
|
416,
|
||||||
|
633,
|
||||||
|
698,
|
||||||
|
594,
|
||||||
|
331,
|
||||||
|
55,
|
||||||
|
516,
|
||||||
|
997,
|
||||||
|
1452,
|
||||||
|
1849,
|
||||||
|
2176,
|
||||||
|
2439,
|
||||||
|
2659,
|
||||||
|
2858,
|
||||||
|
3056,
|
||||||
|
3260,
|
||||||
|
3457,
|
||||||
|
3619,
|
||||||
|
3705,
|
||||||
|
3674,
|
||||||
|
3492,
|
||||||
|
3148,
|
||||||
|
2658,
|
||||||
|
2068,
|
||||||
|
1451,
|
||||||
|
895,
|
||||||
|
485,
|
||||||
|
292,
|
||||||
|
354,
|
||||||
|
665,
|
||||||
|
1174,
|
||||||
|
1792,
|
||||||
|
2402,
|
||||||
|
2881,
|
||||||
|
3119,
|
||||||
|
3039,
|
||||||
|
2612,
|
||||||
|
1864,
|
||||||
|
868,
|
||||||
|
264,
|
||||||
|
1402,
|
||||||
|
2416,
|
||||||
|
3204,
|
||||||
|
3702
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user