2 Commits

Author SHA1 Message Date
f1d844f6a3 config vals changed 2026-03-17 20:54:00 +03:00
cb7f966081 Save current local changes 2026-03-13 17:50:11 +03:00
10 changed files with 1203 additions and 14 deletions

Binary file not shown.

View File

@ -1,6 +1,7 @@
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 time
@ -22,18 +23,20 @@ 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 = 29.2 # 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 = 60 # 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_FREQ_DEFAULT_KHZ = 1000
AD9833_MCLK_DEFAULT_MHZ = 20.0 AD9833_MCLK_DEFAULT_MHZ = 20.0
DS1809_MAX_STEP = 63 DS1809_MAX_STEP = 63
DS1809_DEFAULT_STEP = 39 DS1809_DEFAULT_STEP = 43
DS1809_INIT_HOME_PULSES = 64 DS1809_INIT_HOME_PULSES = 64
DS1809_INIT_PULSE_MS = 2 DS1809_INIT_PULSE_MS = 2
DS1809_INIT_STARTUP_DELAY_S = 0.35 DS1809_INIT_STARTUP_DELAY_S = 0.35
STM32_DAC_VREF = 2.5 STM32_DAC_VREF = 2.5
STM32_DAC_MAX_CODE = 4095 STM32_DAC_MAX_CODE = 4095
PA4_DAC_DEFAULT_VOLT = 0.52 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
@ -162,6 +165,7 @@ def set_initial_params():
params['RampSramMode'] = False params['RampSramMode'] = False
params['RampSramSamples'] = '' params['RampSramSamples'] = ''
params['RampSramAmp'] = '' params['RampSramAmp'] = ''
params['WaveformStatus'] = f'{AD9102_WAVEFORM_FILE_NAME}: не загружен'
params['Ad9833Freq'] = str(AD9833_FREQ_DEFAULT_KHZ) params['Ad9833Freq'] = str(AD9833_FREQ_DEFAULT_KHZ)
params['Ad9833Mclk'] = str(AD9833_MCLK_DEFAULT_MHZ) params['Ad9833Mclk'] = str(AD9833_MCLK_DEFAULT_MHZ)
params['Ad9833Triangle'] = True params['Ad9833Triangle'] = True
@ -573,6 +577,15 @@ if __name__ == "__main__":
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, count=1, pulse_ms=DS1809_INIT_PULSE_MS) 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) ds1809_step = clamp_int(ds1809_step + 1, 0, DS1809_MAX_STEP)

View File

@ -18,6 +18,10 @@ DS1809_CMD_TOTAL_LENGTH = 10 # Total bytes when sending DS1809 UC/DC pulse comma
DS1809_CMD_HEADER = "AAAA" DS1809_CMD_HEADER = "AAAA"
STM32_DAC_CMD_TOTAL_LENGTH = 10 # Total bytes when sending STM32 DAC command STM32_DAC_CMD_TOTAL_LENGTH = 10 # Total bytes when sending STM32 DAC command
STM32_DAC_CMD_HEADER = "BBBB" 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
@ -27,6 +31,13 @@ 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 = 20_000_000 AD9833_MCLK_HZ_DEFAULT = 20_000_000
@ -219,17 +230,45 @@ def send_STM32_DAC(prt, bytestring):
print("Sent: STM32 DAC command.") 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).
''' '''
print("Received "+str(prt.inWaiting())+" bytes.") if not quiet:
print("Received "+str(prt.inWaiting())+" bytes.")
if prt.inWaiting()!=2: if prt.inWaiting()!=2:
print("Error. Couldn't get STATE data. prt.inWaiting():", prt.inWaiting()) if not quiet:
print("Flushing input data:", prt.read(prt.inWaiting())) print("Error. Couldn't get STATE data. prt.inWaiting():", 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
@ -534,6 +573,61 @@ def create_STM32_DAC_command(dac_code: int, enable: bool = True):
return bytearray.fromhex(data) 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:

View File

@ -1,4 +1,5 @@
import time import time
import json
from datetime import datetime from datetime import datetime
import device_commands as cmd import device_commands as cmd
@ -7,19 +8,54 @@ import device_commands as cmd
#### ---- Constants #### ---- Constants
WAIT_AFTER_SEND = 0.15 # Wait after sending command before requesting input (s). 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())
preferred_ports = [port_info for port_info in port_infos if _is_preferred_serial_port(port_info)]
if preferred_ports:
port_infos = preferred_ports
for port_info in sorted(port_infos, key=_port_sort_key):
port = getattr(port_info, "device", None)
if not port:
continue
try: try:
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}")
prt.close() except Exception:
if prt is not None and prt.is_open:
prt.close()
prt = None
continue continue
break break
return prt return prt
@ -35,14 +71,71 @@ def _print_state_reply(state_bytes):
return True return True
def _decode_state_word(state_bytes):
if state_bytes is None:
return None
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):
@ -143,6 +236,76 @@ def set_stm32_dac(prt, dac_code, enable=True):
return _print_state_reply(cmd.get_STATE(prt)) 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): def _wait_for_min_bytes(prt, expected_len, timeout_s, poll_s=0.01):
deadline = time.time() + timeout_s deadline = time.time() + timeout_s
while time.time() < deadline: while time.time() < deadline:

8
gui.py
View File

@ -50,6 +50,8 @@ 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 (МГц):'
@ -275,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))],

911
waveform.json Normal file
View 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
]