commit d6fedaa48be420bfe40d91b4e1664c107f14342c Author: Danila Gamkov Date: Wed Apr 23 19:05:48 2025 +0300 first release of ASOTR data parser (flight) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ce8877 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.csv +*.png +*.swp +*.swo +*.zip +*.log +*.txt +/__pycache__ +/data +/reports diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..adf6810 --- /dev/null +++ b/.vimrc @@ -0,0 +1,132 @@ +set tabstop=4 +set softtabstop=4 +set shiftwidth=4 +set noexpandtab +set colorcolumn=90 +highlight ColorColumnt ctermbg=darkgray +augroup project + autocmd! + autocmd BufRead,BufNewFile *.h,*.c set filetype=c.doxygen +augroup END +let &path.="src/include, src/source," + +" Включаем использование системного буфера +set clipboard=unnamedplus + +" Работа с текстом + +" Python использует 4 пробела для отступов +autocmd FileType python setlocal tabstop=4 shiftwidth=4 + +" Кодировка текста +set encoding=utf-8 +set fileencoding=utf-8 +set fileencodings=utf-8,cp1251,koi8-r,cp866 + +" Поиск по тексту +set hlsearch " подсвечивать результаты поиска + +" Перемещение по тексту +" Когда достигаем границ строки, то перемещаемся на предыдующую/следующую +set whichwrap+=h,l,<,>,[,] + +set number + +" Настройки автодополнения +set completeopt=menu,menuone,noselect + +" Разделение экрана +set splitbelow " разбивать вниз +set splitright " разбивать вправо + + + +" сочетание клавиш + +" Использование h, j, k, l для перемещения с зажатым Ctrl в режиме +" редактирования +inoremap +inoremap +inoremap +inoremap + +let g:mapleader = "\" + +" Переключение между вкладками +nnoremap t :tabnext +nnoremap T :tabprevious + +" Список вкладок +nnoremap tl :tabs + +" nnoremap tn :tabnew +nnoremap tc :tabclose +nnoremap to :tabonly +nnoremap tm :tabmove + +" Редактировать файл в новой вкладке +nnoremap te :tabedit | + +" Выбор вкладки +nnoremap 1 1gt +nnoremap 2 2gt +nnoremap 3 3gt +nnoremap 4 4gt +nnoremap 5 5gt +nnoremap 6 6gt +nnoremap 7 7gt +nnoremap 8 8gt +nnoremap 9 9gt +nnoremap 0 :tablast + +" Разбиение окон +nnoremap s :split +nnoremap v :vsplit + +" Выбор окна +nnoremap h +nnoremap j +nnoremap k +nnoremap l + +" Размер окна +nnoremap + +nnoremap - +nnoremap < +nnoremap > + +" Vimspector +" nnoremap +" nnoremap q +nmap VimspectorStepOver +nmap VimspectorStepInto +nmap VimspectorStepOut +nmap VimspectorDisassemble + +" Сделать окна одного размера +nnoremap = = + +" Переключения между буферами +" nnoremap b :bnext +" nnoremap B :bprevious +" nnoremap l :ls +" nnoremap d :bd + +" " Скрыть/раскрыть блок кода +" nnoremap z za + + +" настройка плагинов + +" настройки для отступов +" let g:indent_guides_enable_on_vim_startup = 1 +" Настройки для разноцветной подсветки скобок +let g:rainbow_active = 1 +" Настройки для vim-airline +let g:airline#extensions#tabline#enabled = 1 +let g:airline#extensions#tabline#buffer_nr_show = 1 +let g:airline#extensions#tabline#formatter = 'unique_tail' +let g:airline_powerline_fonts = 1 +let g:airline_solarized_bg = 'luna' + +let g:vimspector_enable_mappings = 'HUMAN' diff --git a/asotr.py b/asotr.py new file mode 100644 index 0000000..4d1bba6 --- /dev/null +++ b/asotr.py @@ -0,0 +1,519 @@ +import pandas as pd +from datetime import datetime, timedelta +from scipy import signal +from scipy.signal import find_peaks +import matplotlib.pyplot as plt +import numpy as np +import json +import pytz +from matplotlib import dates + +fname_json_decode = '/home/danila/Danila/work/MVN/Soft/PID/python/decode_asotr_cmd.json' + +def get_utc_seconds(timestamp_str, timestamp_format): + dt_obj = datetime.strptime(timestamp_str, timestamp_format) + utc_timezone = pytz.utc + dt_utc = dt_obj.replace(tzinfo=utc_timezone) + timestamp = int(dt_utc.timestamp()) + return timestamp + + +def load_cmd_decode(fname): + with open(fname, 'r') as file: + data = json.load(file) + return data + +def bitmask_to_num(data): + num = int(data) + res = [] + + d = bin(num) + d1 = d[::-1] + + for i in range(num.bit_length()): + if d1[i] == '1': + res.append(i + 1) + return res + +def flight_temperature_decode(cmd_string): + decode = load_cmd_decode(fname_json_decode) + + asotr_kit = '' + temp = [] + cmd = cmd_string.split(' ') + if len(cmd) == 8: + temp = [cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7]] + asotr_kit = cmd[0][1] + + return (asotr_kit, temp) + + +def cmd_decode(cmd_string): + decode = load_cmd_decode(fname_json_decode) + asotr_kit = 0; + msg_decode = '' + out = '' + + if 'OK' in cmd_string: + return out + + cmd = cmd_string.split(' ') + if len(cmd) > 5: + return out + + if cmd[1] == '': + return out + + if '1' in cmd[0]: + asotr_kit = 1 + elif '2' in cmd[0]: + asotr_kit = 2 + + msg_ = f'{cmd[1]} {cmd[2]}' + msg_decode = decode[msg_] + + if (len(cmd) == 4): + value = '' + if cmd[2] == '32': + value1 = bitmask_to_num(cmd[3]) + if (len(value1) == 0): + value = 'запрет всех' + else: + value = ', '.join(map(str, value1)) + elif (cmd[2] == '20' or cmd[2] == '21' or cmd[2] == '22' + or cmd[2] == '23' or cmd[2] == '24' or cmd[3] == '25'): + if cmd[3] == '0': + value = 'ПИД-регулирование' + elif cmd[3] == '1': + value = 'релейное регулирование' + elif cmd[3] == '2': + value = 'постоянная мощность' + else: + value = cmd[3] + out = f'АСОТР{asotr_kit}: {msg_decode} ({value})' + else: + if msg_decode != '': + out = f'АСОТР{asotr_kit}: {msg_decode}' + + return out + +def cmd_flight_parse(asotr_data): + decode_list = [] + temperature_list = [] + + for elem in asotr_data.itertuples(): + elem_msg = cmd_decode(elem.cmd_answer.strip()) + if elem_msg != '': + str_ = f'{elem.timestamp};{elem_msg}' + decode_list.append(str_) + + asotr_kit, temp = flight_temperature_decode(elem.cmd_answer.strip()) + if len(temp) > 0: + timestamp = get_utc_seconds(elem.timestamp, '%d.%m.%Y %H:%M:%S.%f') + str_ = f'{timestamp};{elem.timestamp};{asotr_kit};{temp[0]};{temp[1]};{temp[2]};{temp[3]};{temp[4]};{temp[5]}' + temperature_list.append(str_) + + return (decode_list, temperature_list) + +# accuracy: minutes, hours +def find_best_time_idx(time_arr, user_time, accuracy='minutes'): + tstamp = datetime.strptime(user_time, "%d.%m.%Y %H:%M:%S") + if accuracy == 'minutes': + delta = timedelta(minutes=30) + elif accuracy == 'hours': + delta = timedelta(hours=24) + elif accuracy == 'seconds': + delta = timedelta(seconds=30) + + low = 0 + high = len(time_arr) - 1 + mid = len(time_arr) // 2 + + if mid not in time_arr.index: + # print(f'mid not in time_arr: {mid}') + return -1 + + a = time_arr[mid] + while ((a < (tstamp - delta)) or (a > (tstamp + delta))) and low < high: + if tstamp > a: + low = mid + 1 + else: + high = mid - 1 + mid = (low + high) // 2 + # print(f'mid: (low + high)/2: {mid}') + + if mid not in time_arr.index: + # print(f'mid not in time_arr: {mid}') + return -1 + + a = time_arr[mid] + + if low > high: + # print(f'low > high: {mid}') + mid = high + + if mid > 30: + for j in range(mid-30, len(time_arr)): + # print(f'{time_arr[j]} < {tstamp}: {j}') + if time_arr[j] >= tstamp: + # print(f'{time_arr[j]} > {tstamp}: {j}') + return j + else: + for j in range(0, len(time_arr)): + # print(f'{time_arr[j]} < {tstamp}: {j}') + if time_arr[j] >= tstamp: + # print(f'{time_arr[j]} > {tstamp}: {j}') + return j + + return mid + # raise ValueError("data not found!") + +def find_time_idx(data_list, keys_list, timestamp, accuracy): + out_dict = dict.fromkeys(keys_list, -1) + + for i, elem in enumerate(data_list): + out_dict[keys_list[i]] = find_best_time_idx(elem['timestamp'], timestamp, accuracy) + return out_dict + +def get_cmd_data(fname): + asotr_data = pd.read_csv(fname, delimiter=';') + cmd_list, temperature_list = cmd_flight_parse(asotr_data) + + return (cmd_list, temperature_list) + +def get_data(path, asotr_kit, start_date, end_date, time_accuracy): + ch_signs = ["temp", "temp_set", "pow"] + fname_temp = "asotr" + asotr_kit + "_data_T.csv" + fname_tempSet = "asotr" + asotr_kit + "_data_TSET.csv" + fname_pow = "asotr" + asotr_kit + "_data_P.csv" + + fname = [path + fname_temp, path + fname_tempSet, path + fname_pow] + + dateparse = lambda x: datetime.strptime(x, "%d.%m.%Y %H:%M:%S.%f") + + data = [ pd.read_csv(fname[0], sep=";", parse_dates=["timestamp"], date_parser=dateparse), + pd.read_csv(fname[1], sep=";", parse_dates=["timestamp"], date_parser=dateparse), + pd.read_csv(fname[2], sep=";", parse_dates=["timestamp"], date_parser=dateparse),] + + ch = [[], [], [], [], [], []] + data_dict = { + "temp": ch, + "temp_set": ch, + "pow": ch, + "time_temp": [], + "time_temp_set": [], + "time_pow": [], + } + + idxb = dict.fromkeys(ch_signs, -1) + idxe = dict.fromkeys(ch_signs, -1) + + idxb = find_time_idx(data, ch_signs, start_date, time_accuracy) + idxe = find_time_idx(data, ch_signs, end_date, time_accuracy) + + data_dict["time_temp"] = data[0]["timestamp"][idxb["temp"] : idxe["temp"]] + data_dict["time_temp_set"] = data[1]["timestamp"][idxb["temp_set"] : idxe["temp_set"]] + data_dict["time_pow"] = data[2]["timestamp"][idxb["pow"] : idxe["pow"]] + + col = ["ch1", "ch2", "ch3", "ch4", "ch5", "ch6"] + + for j in range(len(ch_signs)): + data_dict[ch_signs[j]] = data[j][['ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6']][idxb[ch_signs[j]]:idxe[ch_signs[j]]] + + raw_data = data + return (raw_data, data_dict) + +# shift_flag - normalization of the offset of all samples of each period to the first period +# peaks: min, max +def find_periods(time, data, shift_flag, peaks='min'): + + if peaks == 'min': + idx, _ = find_peaks(-data, distance=80) + else: + idx, _ = find_peaks(data, distance=80) + + periods = [] + periods_t = [] + + for i in range(1, len(idx)): + period_t = time.iloc[idx[i-1]:idx[i]] + period = data.iloc[idx[i-1]:idx[i]] + periods.append(period) + periods_t.append(period_t) + + if shift_flag == True: + res = shift_data_(periods) + else: + res = periods + + return (periods_t, res, idx) + +# shift_flag - normalization of the offset of all samples of each period to the first period +def get_signal_profile_corr(time, data, pattern, shift_flag, peak_height): + period_cnts = len(pattern) + periods = [] + periods_t = [] + # find correlation between signal and pattern + correlation = signal.correlate(data, pattern, mode='same', method='fft') + normalized_correlation = correlation / max(abs(correlation)) + + # find correlation peaks + # peak_height = 0.7 + peaks_indices = signal.find_peaks(normalized_correlation, height=peak_height)[0] + + # separate and collect each finded period + for peak_idx in peaks_indices: + start_index = peak_idx - period_cnts // 2 # peak center + end_index = start_index + period_cnts + + if 0 <= start_index < len(data) and 0 <= end_index < len(data): + period = data.iloc[start_index:end_index] + period_t = time.iloc[start_index:end_index] + periods.append(period) + periods_t.append(period_t) + + if shift_flag == True: + res = shift_data_(periods) + else: + res = periods + return (periods_t, res) + +def shift_data_(data): + first = [list_.iloc[0] for list_ in data] + + delta = [] + for i in range(1, len(first)): + delta.append(first[i] - first[0]) + + res = [] + res.append(data[0]) + + for idx, elem in enumerate(data): + if idx > 0: + corr = elem - delta[idx-1] + res.append(corr) + return res + +def get_peak_temp_forecast(cur_time, num_periods): + peaks_forecast = [] + period = timedelta(hours=1, minutes=33, seconds=0, milliseconds=150) + + time = cur_time + for i in range(num_periods): + time = time + period + peaks_forecast.append(time) + + return peaks_forecast + +def plot_signal_profile(time, data, pattern_t, pattern, method, shift_flag, peak_height=0.8): + if method == 'corr': + periods_t, periods = get_signal_profile_corr(time, data, pattern, shift_flag, peak_height) + print(f'Найдено {len(periods)} периодов.') + elif method == 'peaks': + periods_t, periods, peaks = find_periods(time, data, shift_flag, peaks='min') + print(f'Найдено {len(periods)} периодов.') + + fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6)) + + for idx, period in enumerate(periods): + ax1.plot(np.arange(len(period)), period) + ax1.grid(True) + + ax2.plot(time, data) + ax2.grid(True) + plt.grid(True) + plt.show() + +def insert_temp_data_from_flight_cmd(fname_cmd_temp): + dir_asotr = '/home/danila/Danila/work/MVN/Soft/asotr_csv/data/' + fname_asotr = [f'{dir_asotr}asotr01_data_T.csv', f'{dir_asotr}asotr02_data_T.csv'] + + df_cmd = pd.read_csv(fname_cmd_temp, sep=';') + + df_asotr = [] + df_cmd_temp = [] + for i, fname in enumerate(fname_asotr): + df = pd.read_csv(fname, sep=';') + df_asotr.append(df) + + df = df_cmd[df_cmd['asotr_kit'] == i + 1] + df = df.drop(['asotr_kit'], axis=1) + df_cmd_temp.append(df) + + df_asotr_ = [ + pd.concat( + [df_asotr[0], df_cmd_temp[0]], ignore_index=True).sort_values(by='timestamp_sec'), + pd.concat( + [df_asotr[1], df_cmd_temp[1]], ignore_index=True).sort_values(by='timestamp_sec') + ] + + return df_asotr_ + +def subtract_data(data1, data2): + init_shift = data2[0] - data1[0] + out = data2 - data1 - init_shift + return pd.Series(out) + +# for transmit data: cmd_list, temp, power = get_cmd_data(fname) +def cut_data(data, time_begin, duration_sec, accuracy='seconds'): + time_format = "%d.%m.%Y %H:%M:%S"; + + delta = timedelta(seconds=duration_sec) + tstamp_begin = datetime.strptime(time_begin, time_format) + tstamp_end = tstamp_begin + delta + time_end = tstamp_end.strftime(time_format) + + idx_begin = find_best_time_idx(data['timestamp'], time_begin, accuracy) + idx_end = find_best_time_idx(data['timestamp'], time_end, accuracy) + + out = data.loc[idx_begin : idx_end] + return out + + +def cut_norm_data(data, time_begin, duration_sec, channel='ch1', + interp={'method': 'cubic', 'order': 2}, accuracy='seconds'): + data_period = cut_data(data, time_begin, duration_sec, accuracy) + temp_norm = data_period[channel].values - data_period[channel].iloc[0] + time_l = list(data_period['timestamp']) + temp_l = list(temp_norm) + + orig_data = pd.DataFrame({ 'timestamp': time_l, 'temp': temp_l }) + interp_data = orig_data.set_index('timestamp') + interp_data = interp_data.resample('S').mean().interpolate(method=interp["method"], + order=interp["order"]) + interp_data = interp_data.reset_index(names=['timestamp']) + + return orig_data, interp_data + + +def get_step_response_diff(data, thermocycle_info, channel='ch1', + interp={'method': 'cubic', 'order': 2}, accuracy='seconds', cut_step_resp={}): + + date = thermocycle_info['date'] + time_begin_orig = date + ' ' + thermocycle_info['time_begin'][0] + time_begin_step = date + ' ' + thermocycle_info['time_begin'][1] + duration_sec = thermocycle_info['duration_sec'] + + _, orig_interp_cycle = cut_norm_data(data, time_begin_orig, duration_sec, channel, + interp, accuracy) + + _, step_interp_cycle = cut_norm_data(data, time_begin_step, duration_sec, channel, + interp, accuracy) + + max_ = min(len(orig_interp_cycle), len(step_interp_cycle)) + subtract_step = subtract_data( + orig_interp_cycle['temp'].iloc[0:max_].values, + step_interp_cycle['temp'].iloc[0:max_].values) + + step_time = list(step_interp_cycle['timestamp'].iloc[0:max_]) + step_temp = list(subtract_step) + + step_response = pd.DataFrame({'timestamp': step_time, 'temp': step_temp}) + + if len(cut_step_resp) > 0: + time_begin = date + ' ' + cut_step_resp['time_step_begin'] + step_response = cut_data(step_response, time_begin, + cut_step_resp['step_duration'], accuracy='seconds') + first = step_response['temp'].iloc[0] + step_response['temp'] = step_response['temp'] - first + + return (step_response, orig_interp_cycle, step_interp_cycle) + + +def plot_step_response_in_thermocycle(data_info, thermocycle_info, interp, + cut_step_resp, plot_info): + + title = f'{plot_info["title"]}, канал {data_info["channel"][2]} АСОТР КДИ СПИН-X, период опроса {data_info["period"]} ({thermocycle_info["date"]})' + + step_resp, orig_interp_cycle, step_interp_cycle = get_step_response_diff( + data_info['data'], thermocycle_info, channel=data_info['channel'], + interp=interp, accuracy=data_info['find_accuracy']) + + fig = plt.figure(figsize=(8, 6), dpi=200) + fig.suptitle(title, fontsize=plot_info['font']) + ax1 = fig.add_subplot(2,1,1) + ax2 = fig.add_subplot(2,1,2) + + ax1.plot(step_resp['timestamp'], step_resp['temp'], + label='реакция на ступенчатое воздействие') + + step_begin = thermocycle_info['date'] + ' ' + cut_step_resp['time_step_begin'] + + idx = find_best_time_idx(step_interp_cycle.timestamp, step_begin, + accuracy=data_info['find_accuracy']) + ax1.axvline(x = step_interp_cycle.timestamp[idx], color='r', linestyle='-.', + label='момент подачи ступенчатого воздействия') + + date_formatter = dates.DateFormatter(plot_info['ox_dtime_format']) + ax1.xaxis.set_major_formatter(date_formatter) + ax1.legend(loc=plot_info["legend_pos"][0], fontsize=plot_info['font']) + ax1.grid(True) + ax1.tick_params(axis='both', width=1, labelsize=plot_info['font']) + ax1.set_ylabel(r'$\Delta$T, $^\circ$C', fontsize=plot_info['font']) + + ax2.axvline(x = step_interp_cycle.timestamp[idx], color='r', linestyle='-.', + label='момент подачи ступенчатого воздействия') + ax2.plot(orig_interp_cycle['timestamp'], orig_interp_cycle['temp'], '--', + label='термоцикл') + ax2.plot(step_interp_cycle['timestamp'], step_interp_cycle['temp'], + label='термоцикл с реакцией на ступенчатое воздействие') + ax2.xaxis.set_major_formatter(date_formatter) + ax2.legend(loc=plot_info["legend_pos"][1], fontsize=plot_info['font'], + fancybox=True, framealpha=0.4) + ax2.grid(True) + ax2.tick_params(axis='both', width=1, labelsize=plot_info['font']) + ax2.set_xlabel('время', fontsize=plot_info['font']) + ax2.set_ylabel(r'$T_{norm}$, $^\circ$C', fontsize=plot_info['font']) + + fig.suptitle(title, fontsize=plot_info['font']) + plt.tight_layout() + fig.savefig(plot_info["name_fig"]) + plt.show() + + + +def plot_imp_response(data, data_info, plot_info, thermocycle_info): + + title = f'{plot_info["title"]}, канал {data_info["channel"][2]} АСОТР КДИ СПИН-X, период опроса {data_info["period"]} ({thermocycle_info["date"]})' + + fig = plt.figure(figsize=(11, 6), dpi=200) + fig.suptitle(title, fontsize=plot_info['font']) + ax1 = fig.add_subplot(1,1,1) + + + date_formatter = dates.DateFormatter(plot_info['ox_dtime_format']) + ax1.xaxis.set_major_formatter(date_formatter) + ax1.plot(data['timestamp'], data['temp'], '.', label='реакц. на импульсное воздействие') + ax1.xaxis.set_major_formatter(date_formatter) + ax1.legend(loc=plot_info["legend_pos"][0], fontsize=plot_info['font'], + fancybox=True, framealpha=0.4) + ax1.grid(True) + ax1.tick_params(axis='both', width=1, labelsize=plot_info['font']) + ax1.set_xlabel('время', fontsize=plot_info['font']) + ax1.set_ylabel(r'$t_{norm}$, $^\circ$C', fontsize=plot_info['font']) + + fig.suptitle(title, fontsize=plot_info['font']) + plt.tight_layout() + fig.savefig(plot_info["name_fig"]) + plt.show() + + +#timestamp as string format: dd:mm:YYYY HH:MM:SS +def insert_data_cyclo(base_time_str, fname): + path = '/home/danila/Danila/work/MVN/flight/experiments/' + time_format = "%d.%m.%Y %H:%M:%S" + + cyclogram_file = path + fname + df = pd.read_excel(cyclogram_file) + base_time = pd.to_datetime(base_time_str) + df['timestamp'] = df.iloc[:, 0].apply(lambda x: base_time + timedelta(seconds=x)) + df.iloc[:, 0] = df['timestamp'].dt.strftime(time_format) + df = df.drop(['timestamp'], axis=1) + + fname = cyclogram_file.replace('.xls', '.csv') + df.to_csv(fname, index=False, sep=';', encoding='utf-8-sig') + + + + + diff --git a/decode_asotr_cmd.json b/decode_asotr_cmd.json new file mode 100644 index 0000000..3110220 --- /dev/null +++ b/decode_asotr_cmd.json @@ -0,0 +1,88 @@ +{ + "write 0": "маркер начала сектора", + "write 10": "Разрешение/блокировка работы мотора", + "write 11": "режим управления мотором", + "write 12": "направление вращения", + "write 14": "уставка защиты по току мотора кратковременная (мА)", + "write 15": "уставка защиты по току мотора среднему за 20с (мА)", + "write 16": "Время разгона мотора, секунд", + "write 17": "Время останова мотора, секунд", + "write 18": "Номинальный ток мотора( мА)", + "write 20": "СОТР1 режим управления каналом", + "write 21": "СОТР2 режим управления каналом", + "write 22": "СОТР3 режим управления каналом", + "write 23": "СОТР4 режим управления каналом", + "write 24": "СОТР5 режим управления каналом", + "write 25": "СОТР6 режим управления каналом", + "write 26": "СОТР1 уставка мощности в канале в %", + "write 27": "СОТР2 уставка мощности в канале в %", + "write 28": "СОТР3 уставка мощности в канале в %", + "write 29": "СОТР4 уставка мощности в канале в %", + "write 30": "СОТР5 уставка мощности в канале в %", + "write 31": "СОТР6 уставка мощности в канале в %", + "write 32": "Маска-разрешения работы каналов СОТР", + "write 50": "Уставка Kp ПИД-регулятора мотора", + "write 51": "Уставка Kd ПИД-регулятора мотора", + "write 52": "Уставка Ki ПИД-регулятора мотора", + "write 53": "заданная скорость вращения, об/мин", + "write 55": "СОТР1 - уставка температуры канала", + "write 56": "СОТР2 - уставка температуры канала", + "write 57": "СОТР3 - уставка температуры канала", + "write 58": "СОТР4 - уставка температуры канала", + "write 59": "СОТР5 - уставка температуры канала", + "write 60": "СОТР6 - уставка температуры канала", + "write 61": "СОТР1 - уставка Kp ПИД-регулятора канала", + "write 62": "СОТР2 - уставка Kp ПИД-регулятора канала", + "write 63": "СОТР3 - уставка Kp ПИД-регулятора канала", + "write 64": "СОТР4 - уставка Kp ПИД-регулятора канала", + "write 65": "СОТР5 - уставка Kp ПИД-регулятора канала", + "write 66": "СОТР6 - уставка Kp ПИД-регулятора канала", + "write 67": "СОТР1 - уставка Kd ПИД-регулятора канала", + "write 68": "СОТР2 - уставка Kd ПИД-регулятора канала", + "write 69": "СОТР3 - уставка Kd ПИД-регулятора канала", + "write 70": "СОТР4 - уставка Kd ПИД-регулятора канала", + "write 71": "СОТР5 - уставка Kd ПИД-регулятора канала", + "write 72": "СОТР6 - уставка Kd ПИД-регулятора канала", + "write 73": "СОТР1 - уставка Ki ПИД-регулятора канала", + "write 74": "СОТР2 - уставка Ki ПИД-регулятора канала", + "write 75": "СОТР3 - уставка Ki ПИД-регулятора канала", + "write 76": "СОТР4 - уставка Ki ПИД-регулятора канала", + "write 77": "СОТР5 - уставка Ki ПИД-регулятора канала", + "write 78": "СОТР6 - уставка Ki ПИД-регулятора канала", + "write 79": "СОТР1 - уставка гистерезиса релейн. регулятора", + "write 80": "СОТР2 - уставка гистерезиса релейн. регулятора", + "write 81": "СОТР3 - уставка гистерезиса релейн. регулятора", + "write 82": "СОТР4 - уставка гистерезиса релейн. регулятора", + "write 83": "СОТР5 - уставка гистерезиса релейн. регулятора", + "write 84": "СОТР6 - уставка гистерезиса релейн. регулятора", + "write 85": "СОТР1 - уставка Ro термодатчика канала", + "write 86": "СОТР2 - уставка Ro термодатчика канала", + "write 87": "СОТР3 - уставка Ro термодатчика канала", + "write 88": "СОТР4 - уставка Ro термодатчика канала", + "write 89": "СОТР5 - уставка Ro термодатчика канала", + "write 90": "СОТР6 - уставка Ro термодатчика канала", + "write 91": "СОТР1 - уставка Alpha термодатчика канала", + "write 92": "СОТР2 - уставка Alpha термодатчика канала", + "write 93": "СОТР3 - уставка Alpha термодатчика канала", + "write 94": "СОТР4 - уставка Alpha термодатчика канала", + "write 95": "СОТР5 - уставка Alpha термодатчика канала", + "write 96": "СОТР6 - уставка Alpha термодатчика канала", + "func 1": "Перезапуск процессора МУП", + "func 2": "Перезапуск МУП через питание", + "func 3": "Запись текущих уставок в сектор I памяти FLASH", + "func 4": "Запись текущих уставок в сектор J памяти FLASH", + "func 5": "Чтение уставок из сектора I памяти FLASH", + "func 6": "Чтение уставок из сектора J памяти FLASH", + "func 7": "Запуск мотора (разгон и поддержание скорости вращения)", + "func 8": "Останов мотора (торможение и остановка)", + "func 9": "Применить уставки СОТР из ОЗУ в алгоритме ПИД-регуляторов", + "status 1": "", + "status 2": "", + "status 3": "", + "status 4": "", + "status 5": "", + "status 6": "", + "status 7": "", + "status 8": "", + "status 9": "" +} diff --git a/decode_cmd_data.py b/decode_cmd_data.py new file mode 100644 index 0000000..2d96b9d --- /dev/null +++ b/decode_cmd_data.py @@ -0,0 +1,79 @@ +import sys +from importlib import reload +sys.path.append('/home/danila/Danila/work/MVN/Soft/PID/python/') +import asotr +reload(asotr) +import pandas as pd +from datetime import datetime, timedelta + +fname = '/home/danila/Danila/work/MVN/Soft/PID/data/flight/cmd_asotr/all_flight_cmd_asotr.csv' +fname_cmd_temp = './data/flight_cmd_temp.csv' + +## get flight commands file (generated by mvn_log_viewer) +## Translate to human-readeble format and take temperatures from flight commands file +cmd_list, temperature_list = asotr.get_cmd_data(fname) +with open('./data/cmd_human.csv', 'w') as file: + for elem in cmd_list: + file.write(f'{elem}\n') + +## temperatures from flight commands file save to file +with open(fname_cmd_temp, 'w') as file: + file.write(f'timestamp_sec;timestamp;asotr_kit;ch1;ch2;ch3;ch4;ch5;ch6\r\n') + for elem in temperature_list: + file.write(f'{elem}\n') + +## insert temperatures from flight commands file to main asotr temperatures data files +df_asotr_ = asotr.insert_temp_data_from_flight_cmd(fname_cmd_temp) + + +## form timestamp file where minimum of temperatures were registered +end_date = '' +for i, data in enumerate(df_asotr_): + end_date = data['timestamp'].iloc[len(data) - 1][0:18] + data.to_csv(f'./data/asotr0{i+1}_data_T.csv', index=False, sep=';', + encoding='utf-8-sig', decimal='.') + +path_data = '/home/danila/Danila/work/MVN/Soft/asotr_csv/data/' +timeformat = '%d.%m.%Y %H:%M:%S' +prev_days = 14 + +delta_date = datetime.strptime(end_date, timeformat) - timedelta(days=prev_days) +start_date = delta_date.strftime(timeformat) + +for kit in range(1,3): + asotr_kit = f'0{kit}' + print(asotr_kit) + + _, data_dict = asotr.get_data(path_data, asotr_kit, start_date, end_date, 'minutes') + + min_temp_ch = [] + for channel in range(1,7): + ch = f'ch{channel}' + data1 = data_dict['temp'][ch] + time1 = data_dict['time_temp'] + + periods_t, periods, _ = asotr.find_periods(time1, data1, shift_flag=False, peaks='min') + + min_temp_period = [] + for elem in periods_t: + min_temp_period.append(elem.iloc[0].strftime('%d.%m.%Y %H:%M:%S.%f')[:-3]) + + min_temp_ch.append(min_temp_period) + + df = pd.DataFrame(min_temp_ch).transpose() + df.to_csv(f'./data/asotr{asotr_kit}_min_T.csv', header=False, index=False, sep=';', + encoding='utf-8-sig', decimal='.') + df1 = pd.read_csv(f'./data/asotr{asotr_kit}_min_T.csv', sep=';', + names=['ch1','ch2','ch3','ch4','ch5','ch6']) + df1.to_csv(f'./data/asotr{asotr_kit}_min_T.csv', index=False, sep=';', + encoding='utf-8-sig', decimal='.') + + + + + + + + + + diff --git a/plot_flight_borders.py b/plot_flight_borders.py new file mode 100644 index 0000000..a824d98 --- /dev/null +++ b/plot_flight_borders.py @@ -0,0 +1,172 @@ +import matplotlib.pyplot as plt +from matplotlib import dates +import argparse +import sys +from importlib import reload +sys.path.append('/home/danila/Danila/work/MVN/Soft/PID/python/') +import asotr +reload(asotr) +import pandas as pd + +def convert_to_str(lst): + index = [i for i, x in enumerate(lst) if x == 1] + + res = f"ch{index[0] + 1}" + for idx in index[1:]: + res += f"_{idx + 1}" + return res + +def plot_asotr_borders(ch, asotr_kit, begin, end, font=14, cmd=0): + print_width = 20 + print_height = 12 + width = 1 + plot_windows = 2 + + channels = list(map(int, ch)) + + pict_name = (f'./reports/ASOTR{asotr_kit}_flight_T_P_{convert_to_str(channels)}_{begin[0:5].replace(".", "")}_{end[0:5].replace(".", "")}_{end[6:]}.png') + + plot_task = {"temp": 1, "temp_set": 1, "pow": 1} + + ox_dtime_format = "%d.%m.%Y" + + legend = [ + "канал 1 (БРД1)", + "канал 2 (БРД2)", + "канал 3 (БРД3)", + "канал 4 (БРД4)", + "канал 5 (плита МУП МВН)", + "канал 6 (плита МУП МВН)", + ] + legend_set = list(map(lambda x: x + " уставка", legend)) + width = [1, 1, 1, 1, 1, 1] + width_set = [3, 3, 3, 3, 3, 3] + + marker = ["-", "--", "-.", "-", "-", "--"] + width_arr = [1, 0.5, 0.2, 0.1, 1, 1] + + # get from files and prepare data + path = "/home/danila/Danila/work/MVN/Soft/asotr_csv/data/" + start_date = begin + " 00:00:00" # Начальная граница + end_date = end + " 23:59:59" # Конечная граница + data, data_dict = asotr.get_data(path, asotr_kit, start_date, end_date, 'minutes') + + if plot_windows == 1: + fig, ax = plt.subplots(figsize=(print_width, print_height), dpi=200) + + if plot_task["temp"] == 1: + for i in range(len(channels)): + if channels[i] == 1: + ax.plot(data_dict["time_temp"], + data_dict['temp'].iloc[:,[i]], + marker[i], + linewidth=width[i], + label=legend[i],) + + ax.tick_params(axis="both", width=1, labelsize=font) + ax.grid(visible=True, linestyle="dotted") + ax.set_ylabel("Температура, $^\circ$C", fontsize=font) + ax.set_xlabel("Время", fontsize=font) + ax.legend(fontsize=font) + + date_formatter = dates.DateFormatter(ox_dtime_format) + ax.xaxis.set_major_formatter(date_formatter) + + plt.tight_layout() + fig.savefig(pict_name) + plt.show() + + elif plot_windows == 2: + + fig = plt.figure(figsize=(print_width, print_height), dpi=200) + ax1 = fig.add_subplot(2, 1, 1) + ax2 = fig.add_subplot(2, 1, 2, sharex=ax1) + + + if cmd == '1': + cmd_human = pd.read_csv('./flight_cmd_human.txt', + delimiter=';', names=['timestamp', 'cmd']) + + for i, row in cmd_human.iterrows(): + if i > 20: + row_time = row['timestamp'][0:len(row['timestamp']) - 4] + idx = asotr.find_best_time_idx(data_dict['time_temp'], + row_time, accuracy='minutes') + if idx != -1: + ax1.axvline(x = data_dict['time_temp'][idx], color='r', + linestyle='-.') + ax1.text(data_dict['time_temp'][idx], 30, row['cmd'], + rotation=45, va='bottom', fontsize=font) + + if plot_task["temp"] == 1: + for i in range(len(channels)): + if channels[i] == 1: + ax1.plot(data_dict["time_temp"], + data_dict['temp'].iloc[:,[i]], + marker[i], + linewidth=width[i], + label=legend[i],) + + if plot_task["temp_set"] == 1: + for i in range(len(channels)): + if channels[i] == 1: + ax1.plot(data_dict["time_temp_set"], + data_dict['temp_set'].iloc[:,[i]], + marker[i], + linewidth=width_set[i], + label=legend_set[i],) + + if plot_task["pow"] == 1: + for i in range(len(channels)): + if channels[i] == 1: + ax2.plot(data_dict["time_pow"], + data_dict['pow'].iloc[:,[i]], + marker[i], + linewidth=width[i], + label=legend[i],) + + ax1.tick_params(axis="both", width=1, labelsize=font) + ax1.grid(visible=True, linestyle="dotted") + ax1.set_ylabel("Температура, $^\circ$C", fontsize=font) + ax1.set_xlabel("Время", fontsize=font) + ax1.legend(fontsize=font) + + date_formatter = dates.DateFormatter(ox_dtime_format) + ax1.xaxis.set_major_formatter(date_formatter) + + ax2.tick_params(axis="both", width=1, labelsize=font) + ax2.grid(visible=True, linestyle="dotted") + ax2.set_ylabel("Мощность, %", fontsize=font) + ax2.set_xlabel("Время", fontsize=font) + ax2.set_ylim(-5,105) + + ax2.legend(fontsize=font) + + date_formatter = dates.DateFormatter(ox_dtime_format) + ax2.xaxis.set_major_formatter(date_formatter) + + title = (f'работа АСОТР{asotr_kit} в период с {start_date[0:10]} по {end_date[0:10]} г.') + fig.suptitle(title, fontsize=font) + plt.tight_layout() + fig.savefig(pict_name) + plt.show() + +if __name__ == '__main__': + argparser = argparse.ArgumentParser("plot_flight_borders.py") + + argparser.add_argument('-c', '--channel', required=True, + help='type channel (example: 000011)') + argparser.add_argument('-a', '--asotr', required=True, + help='type asotr kit (01 or 02)') + argparser.add_argument('-b', '--begin', required=True, + help='type begin date if dd.mm.YYYY format') + argparser.add_argument('-e', '--end', required=True, + help='type end date if dd.mm.YYYY format') + argparser.add_argument('-f', '--font', required=False, + help='type font size (from 1 to 30)') + argparser.add_argument('-d', '--cmd', required=False, + help='type display commands flag (0/1)') + args = argparser.parse_args() + + plot_asotr_borders(args.channel, args.asotr, args.begin, args.end, + args.font, args.cmd) diff --git a/plot_flight_borders.sh b/plot_flight_borders.sh new file mode 100755 index 0000000..a9dbc7a --- /dev/null +++ b/plot_flight_borders.sh @@ -0,0 +1,19 @@ +#! /bin/bash + +if [ $# != 2 ] +then + echo "erorr use $0. Right use this script: " + echo "$0 25.02.2025 10.03.2025" +else + begin=$1 + end=$2 + python3 plot_flight_borders.py -c 111100 -a 01 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 001000 -a 01 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 000011 -a 01 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 111100 -a 02 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 010100 -a 02 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 010000 -a 02 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 000100 -a 02 -b ${begin} -e ${end} + python3 plot_flight_borders.py -c 000011 -a 02 -b ${begin} -e ${end} +fi + diff --git a/plot_periods_profile.py b/plot_periods_profile.py new file mode 100644 index 0000000..bfa45c0 --- /dev/null +++ b/plot_periods_profile.py @@ -0,0 +1,94 @@ +import sys +from importlib import reload +sys.path.append('/home/danila/Danila/work/MVN/Soft/PID/python/') +import asotr +reload(asotr) +import matplotlib.pyplot as plt +from matplotlib import dates +import numpy as np +from datetime import timedelta + +pict_name = 'periods_profile_10042025.png' +path = '/home/danila/Danila/work/MVN/Soft/asotr_csv/data/' +channel = 'ch1' +asotr_kit = '01' +start_date = '10.04.2025 04:00:00' +end_date = '10.04.2025 12:00:00' +# start_date = '06.01.2025 22:40:00' +# end_date = '21.01.2025 01:20:00' +shift = True + +raw_data, data_dict = asotr.get_data(path, asotr_kit, start_date, end_date, 'minutes') + +data1 = data_dict['temp'][channel] +time1 = data_dict['time_temp'] + +periods_t, periods, _ = asotr.find_periods(time1, data1, shift_flag=False, peaks='min') + +_, _, peaks = asotr.find_periods(time1, data1, shift_flag=False, peaks='max') + +peaks_forecast = asotr.get_peak_temp_forecast(time1.iloc[peaks[0]], 1000) + +delta_sec = [] +for idx, elem in enumerate(peaks): + if idx > 0: + print(f'peak____: {time1.iloc[elem]}') + print(f'forecast: {peaks_forecast[idx-1]}') + delta = time1.iloc[elem] - peaks_forecast[idx-1] + delta_sec.append(delta.total_seconds()) + + +# asotr.plot_signal_profile(time1, data1, [], [], method='peaks', shift_flag=shift) +# asotr.plot_signal_profile(time1, data1, periods_t[0], periods[0], method='corr', shift_flag=shift, peak_height=0.7) + +time_, periods_ = asotr.get_signal_profile_corr(time1, data1, periods[0], shift, peak_height=0.7) +print(f'Найдено {len(periods_)} периодов.') + +ox_dtime_format = "%H:%M:%S" +fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(14, 10)) + +date_formatter = dates.DateFormatter(ox_dtime_format) +ax1.xaxis.set_major_formatter(date_formatter) + +ax1.plot(time1, data1) +for elem in peaks: + ax1.axvline(x = time1.iloc[elem], color='r', linewidth=0.5) + +ax1.set_title(f'Температура на орбите: АСОТР{asotr_kit}, канал {channel[2]}') + +for idx, period in enumerate(periods_): + ax2.plot(np.arange(len(period)), period, label=f'период {idx}') + +ax2.set_title('Профиль изменения температуры АСОТР по периоду') + +delta = [] +for elem in periods_: + delta1 = elem.values - periods[0].values + delta.append(delta1) + +# ax3.plot(delta[1], label=f'период 1', marker='o', linewidth=2) +for idx, elem in enumerate(delta): + if idx == len(delta) - 1: + ax3.plot(elem, label=f'период {idx}', marker='|', linewidth=2) + elif idx == len(delta)//2: + ax3.plot(elem, label=f'период {idx}', marker='^', linewidth=2) + elif idx == 1: + ax3.plot(elem, label=f'период {idx}', marker='o', linewidth=2) + elif idx > 0: + ax3.plot(elem, label=f'период {idx}') + +# ax4.plot(delta_sec) + +ax3.set_title(r'$\Delta$$T_i$ = $T_i$ - $T_1$') +ax1.set_ylabel('Температура, $^\circ$C') +ax2.set_ylabel('Температура, $^\circ$C') +ax3.set_ylabel(r'$\Delta$$T_i$, $^\circ$C') +ax3.set_xlabel("Время, мин.") +ax1.grid(True) +ax2.grid(True) +ax3.grid(True) +# ax4.grid(True) +ax2.legend() +ax3.legend() +fig.savefig(pict_name) +plt.show() diff --git a/prepare_flight_cyclo.py b/prepare_flight_cyclo.py new file mode 100644 index 0000000..5c327e7 --- /dev/null +++ b/prepare_flight_cyclo.py @@ -0,0 +1,17 @@ +import sys +from importlib import reload +sys.path.append('/home/danila/Danila/work/MVN/Soft/PID/python/') +import asotr +reload(asotr) +import pandas as pd +from datetime import datetime, timedelta + +timestamp = '25.04.2025 01:18:00' +cyclogram_file = 'cyclogram_step_ident_ch1.xls' +asotr.insert_data_cyclo(timestamp, cyclogram_file) + +timestamp = '25.04.2025 04:24:00' +cyclogram_file = 'cyclogram_imp_ident_ch1.xls' +asotr.insert_data_cyclo(timestamp, cyclogram_file) + + diff --git a/temp_peaks_forecast.py b/temp_peaks_forecast.py new file mode 100644 index 0000000..cac510d --- /dev/null +++ b/temp_peaks_forecast.py @@ -0,0 +1,86 @@ +import sys +import statistics +from importlib import reload +sys.path.append('/home/danila/Danila/work/MVN/Soft/PID/python/') +import asotr +reload(asotr) +from datetime import datetime, timedelta +import matplotlib.pyplot as plt +from matplotlib import dates +from datetime import timedelta + +path = '/home/danila/Danila/work/MVN/Soft/asotr_csv/data/' +channel = 'ch1' +asotr_kit = '01' +start_date = '20.04.2025 00:00:00' +forecast_days = 20 +# end_date = '26.03.2025 01:20:00' + +timeformat = '%d.%m.%Y %H:%M:%S' +delta_date = datetime.strptime(start_date, timeformat) + timedelta(days=forecast_days) +end_date = delta_date.strftime(timeformat) +num_peaks_forecast = forecast_days * 20 +# start_date = '06.01.2025 22:40:00' +# end_date = '21.01.2025 01:20:00' +shift = True + +raw_data, data_dict = asotr.get_data(path, asotr_kit, start_date, end_date, 'minutes') + +data1 = data_dict['temp'][channel] +time1 = data_dict['time_temp'] + +periods_t, periods, _ = asotr.find_periods(time1, data1, shift_flag=False, peaks='min') +_, _, peaks = asotr.find_periods(time1, data1, shift_flag=False, peaks='max') + +peaks_forecast = asotr.get_peak_temp_forecast(time1.iloc[peaks[0]], num_peaks_forecast) + +with open('peaks_forecast.txt', 'w') as file: + for elem in peaks_forecast: + file.write(f'{str(elem)}\n') + +delta_sec = [] +for idx, elem in enumerate(peaks): + if idx > 0: + delta = time1.iloc[elem] - peaks_forecast[idx-1] + # print(delta) + delta_sec.append(delta.total_seconds()) + +delta_self_sec = [] +delta_self_sec1 = [] +for idx, elem in enumerate(periods_t): + delta1 = elem.iloc[len(elem)-1] - elem.iloc[0] + delta_self_sec.append(delta1.total_seconds()) + +for idx, elem in enumerate(delta_self_sec): + if idx > 0: + delta_self_sec1.append(delta_self_sec[idx] - delta_self_sec[idx - 1]) + +# print(delta_self_sec) +print(statistics.median(delta_self_sec)) + +ox_dtime_format = "%d.%m.%Y %H:%M" +fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 6)) + +date_formatter = dates.DateFormatter(ox_dtime_format) +ax1.xaxis.set_major_formatter(date_formatter) + +ax1.plot(time1, data1) +for elem in peaks: + ax1.axvline(x = time1.iloc[elem], color='r', linewidth=0.5) + +ax1.set_title(f'температура на орбите: АСОТР{asotr_kit}, канал {channel[2]}') + +ax2.set_title('Разница по времени между временем i-го пика и i-м предсказанием пика') +ax2.set_ylabel(r'$\Delta$$t_{peak}$ = $timePeak_i$ - $timeForecast_i$, сек') +ax2.plot(delta_sec) + +ax3.set_title('Разница по времени между первым и последующим периодами') +ax3.set_ylabel(r'$\Delta$$t_{period}$ = $period_i$ - $period_0$, сек') +ax3.plot(delta_self_sec1) + +ax1.set_ylabel('Температура, град.') + +ax1.grid(True) +ax2.grid(True) +ax3.grid(True) +plt.show()