123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- # -*- coding: utf-8 -*-
- """
- Created on Mon Jul 3 17:53:21 2023
- @author: zilya
- """
- import tkinter as tk
- from math import ceil, floor
- import numpy as np
- from types import SimpleNamespace
- from datetime import datetime
- import json
- import pickle
- def set_limits():
-
- # Задание общих аппаратных характкристик
- gamma = 42.576e6 # Hz/T Гиромагнитное отношение водорода
- G_amp_max_mT_m = 37.8 # mT/m. Максимальный градиент
- G_amp_max = G_amp_max_mT_m*1e-3*gamma # Hz/m. Максимальный градиент
- G_slew_max_T_m_s = 121 # T/m/s. Максимальная скорость нарастания
- G_slew_max = G_slew_max_T_m_s*gamma # Hz/m/s. Максимальная скорость нарастания
- rf_raster_time = 1e-6 # s. Растр РЧ импульса
- grad_raster_time = 10e-6 # s. Растр градиентов
- tau_max = G_amp_max/G_slew_max # s. Максимальное время нарастания градиента с учетом макс скорости нарастания
- tau_max = np.ceil(tau_max/ grad_raster_time)*grad_raster_time
- tau = 250e-6 # s. Фиксированное время нарастания градиентов
-
- # Чтение заданных в интерфэйс значений
- sl_nb = int(textBox1.get())
- sl_thkn = float(textBox2.get())
- sl_gap = float(textBox3.get())
- FoV_f = float(textBox4.get())
- FoV_ph_image = float(textBox5.get())
- Nf = int(textBox6.get())
- Np_image = int(textBox7.get())
- BW_pixel = float(textBox8.get())
- N_TE = int(textBox9.get())
- #TE = float(textBox9.get())
- TR = float(textBox10.get())
- alpha = float(textBox11.get())
- beta = float(textBox12.get())
- Conct = int(textBox13.get())
- ETL = int(textBox14.get())
- Average = int(textBox15.get())
- ph_over = float(textBox16.get())
-
- Np = Np_image*(1+ph_over/100)
- Np = round(Np)
- ph_over = (Np/Np_image-1)*100
- FoV_ph = FoV_ph_image*(1+ph_over/100)
-
- TR = np.ceil(TR/ grad_raster_time)*grad_raster_time
-
-
- # --- Параметры РЧ импульса -----
- rf_ringdown_time=20e-6
- rf_dead_time=100e-6
- adc_dead_time=10e-6
-
- # - Библиотека time bandwidth products
- t_BW_product_ex1 = 3.8
- t_BW_product_ref1 = 4.2
- t_BW_product_ex2 = 3.55
- t_BW_product_ref2 = 3.55
-
- # s. Библиотека длительностей импульсов
- # Длительности кратны растру градиента 10 мкс
- t_ex1 = 2.05e-3
- t_ref1 = 2.56e-3
- t_ex2 = 3.10e-3
- t_ref2 = 3.88e-3
-
- apodization = 0.27
- # ----- Подбор импульсов на основе выбранной толщины среза
- if sl_thkn > t_BW_product_ex1/(t_ex1*G_amp_max):
- t_BW_product_ex = t_BW_product_ex1
- t_BW_product_ref = t_BW_product_ref1
- else:
- t_BW_product_ex = t_BW_product_ex2
- t_BW_product_ref = t_BW_product_ref2
-
- if sl_thkn > t_BW_product_ex2/(t_ex1*G_amp_max):
- t_ex = t_ex1
- t_ref = t_ref1
- else:
- t_ex = t_ex2
- t_ref = t_ref2
-
- BW_ex_pulse = t_BW_product_ex/t_ex
-
- # ---- Вычисление основных площадей и длительностей ------
- A_ex = BW_ex_pulse*(t_ex+tau)/sl_thkn
- A_read = Nf/FoV_f
- A_ph = Np/2/FoV_ph
- A_reph = 0.25*A_ex
- A_pre = 1.5*A_read
- A_crs = 0.75*A_ex
- A_crr = A_read
- A_sps = 4*A_ex
-
- t_reph_min = A_reph/G_amp_max + tau
- t_pre_min = A_pre/G_amp_max + tau
- t_pre_min = max(t_reph_min,t_pre_min)
- t_pre_min = np.ceil(t_pre_min / grad_raster_time)*grad_raster_time
-
- t_crs_min = A_crs/G_amp_max + tau
- t_crr_min = A_crr/G_amp_max + tau
- t_ph_min = A_ph/G_amp_max + tau
- t_cr_min = max(t_crs_min,t_crr_min,t_ph_min)
- t_cr_min = np.ceil(t_cr_min / grad_raster_time)*grad_raster_time
-
- t_read = 1/BW_pixel
- t_read = np.ceil(t_read / grad_raster_time)*grad_raster_time
-
- t_sps = A_sps/G_amp_max + tau
- t_sps = np.ceil(t_sps / grad_raster_time)*grad_raster_time
-
-
- ES_1 = t_ref + t_read + 2*t_cr_min + 4*tau
- ES_2 = t_ref + t_ex + 2*t_cr_min + 2*t_pre_min + 4*tau
- ES = max(ES_1,ES_2)
- ES = np.ceil(ES/ grad_raster_time)*grad_raster_time
-
- t_cr = 0.5*(ES-t_ref-t_read) - 2*tau
- t_cr = np.ceil(t_cr / grad_raster_time)*grad_raster_time
-
- t_pre = 0.5*(ES-t_ref-t_ex) - 2*tau
- t_pre = np.ceil(t_pre / grad_raster_time)*grad_raster_time
-
- TE = N_TE*ES
- N_TE_min = 1
- N_TE_max = ETL
- TR_min = ES*(ETL) + t_ex/2 + t_read/2 + t_sps + 2*tau
- TR_max = 10
- TR_min = np.ceil(TR_min/ grad_raster_time)*grad_raster_time
-
- delay_TR = TR-TR_min
-
- Conc_min = ceil(sl_nb*TR_min/TR)
- Conc_max = sl_nb
- ETL_min = 1;
- ETL_max1 = 16;
- ETL_max2 = floor((TR-t_ex/2-t_read/2-t_sps-2*tau)/ES)
- ETL_max = min(ETL_max1,ETL_max2)
-
- Nf_min = 1
- #Nf_max1 = FoV_f*G_amp_max*(t_cr-tau)
- Nf_max = min(FoV_f*G_amp_max*(t_read),1600)
- #Nf_max = min(Nf_max1,Nf_max2)
- Np_min = 1
- Np_max1 = Nf
- Np_max2 = 2*FoV_ph*G_amp_max*(t_cr-tau)
- Np_max = min(Np_max1,Np_max2)
- FoV_min = 25e-3
- FoV_f_max = 450e-3
- FoV_f_min = Nf/(G_amp_max*t_read)
- #FoV_f_min2 = Nf/(G_amp_max*(t_cr-tau))
- FoV_f_min = max(FoV_f_min,FoV_min)
- FoV_p_min = FoV_f_min
- FoV_p_max = FoV_f
-
- Np_image_min = Np_min
- Np_image_max = min(Np_max, Nf_max/(1+ph_over/100))
-
- FoV_p_image_min = FoV_p_min/(1+ph_over/100)
- FoV_p_image_max = min(FoV_p_max,FoV_f_max/(1+ph_over/100))
-
-
- # BW_pixel_min = 1/(ES-t_ref-2*t_cr_min-4*tau)
- # BW_pixel_min = max(ceil(BW_pixel_min), 40)
- BW_pixel_min = 40
- BW_pixel_max1 = 1780
- BW_pixel_max2 = FoV_f*G_amp_max/Nf
- BW_pixel_max = min (BW_pixel_max1,BW_pixel_max2)
-
- sl_thkn_min1 = t_BW_product_ex2/(t_ex2*G_amp_max)
- sl_thkn_min2 = t_BW_product_ex2*(t_ex2+tau)/((t_cr-tau)*G_amp_max)
- sl_thkn_min = max(sl_thkn_min1,sl_thkn_min2)
- sl_thkn_max = 20e-3
- sl_gap_min, sl_gap_max = 0, 800
-
- Average_min, Average_max = 1, 10
-
- ph_over_min, ph_over_max = 0, 100
-
-
- label2_1.configure(text = "min = " + str(ceil(sl_thkn_min*10000)/10000))
- label2_2.configure(text = "max = " + str(sl_thkn_max))
- label3_1.configure(text = "min = " + str(sl_gap_min))
- label3_2.configure(text = "max = " + str(sl_gap_max))
- label4_1.configure(text = "min = " + str(ceil(FoV_f_min*1000)/1000))
- label4_2.configure(text = "max = " + str(FoV_f_max))
- label5_1.configure(text = "min = " + str(ceil(FoV_p_image_min*1000)/1000))
- label5_2.configure(text = "max = " + str(ceil(FoV_p_image_max*1000)/1000))
- label6_1.configure(text = "min = " + str(Nf_min))
- label6_2.configure(text = "max = " + str(ceil(Nf_max)))
- label7_1.configure(text = "min = " + str(ceil(Np_image_min*1000)/1000))
- label7_2.configure(text = "max = " + str(ceil(Np_image_max*1000)/1000))
- label8_1.configure(text = "min = " + str(BW_pixel_min))
- label8_2.configure(text = "max = " + str(ceil(BW_pixel_max*1000)/1000))
- label9_1.configure(text = "min = " + str(N_TE_min))
- label9_2.configure(text = "max = " + str(N_TE_max))
- label10_1.configure(text = "min = " + str(ceil(TR_min*1000)/1000))
- label10_2.configure(text = "max = " + str(TR_max))
- label13_1.configure(text = "min = " + str(Conc_min))
- label13_2.configure(text = "max = " + str(Conc_max))
- label14_1.configure(text = "min = " + str(ETL_min))
- label14_2.configure(text = "max = " + str(ETL_max))
- label15_1.configure(text = "min = " + str(Average_min))
- label15_2.configure(text = "max = " + str(Average_max))
- label16_1.configure(text = "min = " + str(ph_over_min))
- label16_2.configure(text = "max = " + str(ph_over_max))
-
- global param
- param = SimpleNamespace()
- param.G_amp_max =G_amp_max_mT_m
- param.G_slew_max = G_slew_max_T_m_s
- param.gamma = gamma
- param.grad_raster_time = grad_raster_time
- param.rf_raster_time = rf_raster_time
-
-
- param.t_BW_product_ex = t_BW_product_ex
- param.t_BW_product_ref = t_BW_product_ref
- param.t_ex = t_ex
- param.t_ref = t_ref
- param.rf_ringdown_time = rf_ringdown_time
- param.rf_dead_time = rf_dead_time
- param.adc_dead_time = adc_dead_time
- param.aapodization = apodization
-
- param.dG = tau
- param.sl_nb = sl_nb
- param.sl_thkn = sl_thkn
- param.sl_gap = sl_gap
- param.FoV_f = FoV_f
- param.FoV_ph = FoV_ph
- param.Nf = Nf
- param.Np = Np
- param.BW_pixel = BW_pixel
- param.TE = TE
- param.N_TE = N_TE #то на коком номере эхо истинное время эхо(в центр к-пр)
- param.ES = ES
- param.TR = TR
- param.FA = alpha
- param.RA = beta
- param.conct = Conct
- param.ETL = ETL
- param.Average = Average
- param.ph_over = ph_over
-
- param.delay_TR = delay_TR
- # tk.Label(win, text = 't = ' + str(round(time//60)) + ':'+ str(round(time%60)) + 'min').grid(row=13, column=2,sticky = "E")
- tk.Label(win, text = 'ES = ' + str(ceil(ES*10000)/10000) + ' s').grid(row=19, column=2,sticky = "E")
- tk.Label(win, text = 'TE = ' + str(ceil(TE*10000)/10000) + ' s').grid(row=20, column=2,sticky = "E")
- #tk.Label(win, text = str(ETL_max2) + 's').grid(row=18, column=2,sticky = "E")
- def save_param():
-
- output_filename = str(textBox17.get())
- # output_filename = "TSE_T1" + datetime.now().strftime("%Y%m%d_%H%M%S")
- file = open(output_filename + ".json", 'w')
- json.dump(param.__dict__, file, indent = 4)
- file.close()
-
- # output_filename = "TSE_" + datetime.now().strftime("%Y%m%d_%H%M%S")
- # file = open(output_filename, 'wb')
- # pickle.dump(param, file)
- # file.close()
-
- def read_json():
- filepath = tk.filedialog.askopenfilename()
- if filepath != "":
- with open(filepath, "r") as file:
- params_j = json.load(file)
- textBox2.delete(0, 'end'); textBox2.insert(0, params_j['sl_thkn'])
- textBox3.delete(0, 'end'); textBox3.insert(0, params_j['sl_gap'])
- textBox4.delete(0, 'end'); textBox4.insert(0, params_j['FoV_f'])
- textBox5.delete(0, 'end'); textBox5.insert(0, params_j['FoV_ph'])
- textBox6.delete(0, 'end'); textBox6.insert(0, params_j['Nf'])
- textBox7.delete(0, 'end'); textBox7.insert(0, params_j['Np'])
- textBox8.delete(0, 'end'); textBox8.insert(0, params_j['N_TE'])
- textBox9.delete(0, 'end'); textBox9.insert(0, params_j['N_TE'])
- textBox10.delete(0, 'end'); textBox10.insert(0, params_j['TR'])
- textBox11.delete(0, 'end'); textBox11.insert(0, params_j['FA'])
- textBox12.delete(0, 'end'); textBox12.insert(0, params_j['RA'])
- textBox13.delete(0, 'end'); textBox13.insert(0, params_j['conct'])
- textBox14.delete(0, 'end'); textBox14.insert(0, params_j['ETL'])
- textBox15.delete(0, 'end'); textBox15.insert(0, params_j['Average'])
- textBox16.delete(0, 'end'); textBox16.insert(0, params_j['ph_over'])
- ### Defoult values ###
- # --------------------------------
- sl_nb = 1 # number of slices
- sl_thkn = 5e-3 # Slice thickness, m
- sl_gap = 100 # Slice gap, %
-
- #---------------------------------
- FoV_f = 32e-3 # FoV in freq encoding direction, m
- FoV_ph = 32e-3 # FoV in ph encoding direction, m
- Nf = 16 # Freq direction resolution
- Np = 16 # Resolution in ph encoding direction
- #----------------------------------
- BW_pixel = 500 # Pixel BW
- N_TE = 1 # number of echo in center of k-space
- # TE = 20e-3 # Echo time, s
- TR = 500e-3 # Repetition time, s
- alpha = 90 # Flip angle, degree
- beta = 180
- ETL = 8
- Conct = 1
- Average = 1
- ph_over = 0
-
- win = tk.Tk()
- win.title('TSE')
- win.geometry("350x470+100+100")
- win.resizable(False,False)
- # number of slices
- sl_nb_min,sl_nb_max = 1, 103
- tk.Label(win, text = 'Slices').grid(row=0, column=0,sticky = "E")
- textBox1 = tk.Spinbox(from_=sl_nb_min, to=sl_nb_max, width = 4)
- textBox1.grid(row=0, column=1)
- label1_1 = tk.Label(win, text = "min = " + str(sl_nb_min))
- label1_1.grid(row=0, column=2)
- label1_2 = tk.Label(win, text = "max = " + str(sl_nb_max))
- label1_2.grid(row=0, column=3)
- # Slice thickness, m
- tk.Label(win, text = 'Slice thickness, m').grid(row=1, column=0,sticky = "E")
- textBox2 = tk.Entry(width = 6)
- textBox2.insert(0, sl_thkn)
- textBox2.grid(row=1, column=1)
- label2_1 = tk.Label(win, text = "min = " )
- label2_1.grid(row=1, column=2)
- label2_2 = tk.Label(win, text = "max = " )
- label2_2.grid(row=1, column=3)
- # Slice gap, %
- tk.Label(win, text = 'Slice gap, % ').grid(row=2, column=0,sticky = "E")
- textBox3 = tk.Entry(width = 6)
- textBox3.insert(0, sl_gap)
- textBox3.grid(row=2, column=1)
- label3_1 = tk.Label(win, text = "min = ")
- label3_1.grid(row=2, column=2)
- label3_2 = tk.Label(win, text = "max = ")
- label3_2.grid(row=2, column=3)
- # FoV in freq encoding direction, m
- tk.Label(win, text = 'FoV read, m').grid(row=3, column=0,sticky = "E")
- textBox4 = tk.Entry(width = 6)
- textBox4.insert(0, FoV_f)
- textBox4.grid(row=3, column=1)
- label4_1 = tk.Label(win, text = "min = ")
- label4_1.grid(row=3, column=2)
- label4_2 = tk.Label(win, text = "max = ")
- label4_2.grid(row=3, column=3)
- # FoV in ph encoding direction, m
- label5 = tk.Label(win, text = 'FoV phase, m')
- label5.grid(row=4, column=0,sticky = "E")
- textBox5 = tk.Entry(width = 6)
- textBox5.insert(0, FoV_f)
- textBox5.grid(row=4, column=1)
- label5_1 = tk.Label(win, text = "min = ")
- label5_1.grid(row=4, column=2)
- label5_2 = tk.Label(win, text = "max = ")
- label5_2.grid(row=4, column=3)
- # Freq direction resolution
- label6 = tk.Label(win, text = 'Read resolution')
- label6.grid(row=5, column=0,sticky = "E")
- textBox6 = tk.Entry(width = 6)
- textBox6.insert(0, Nf)
- textBox6.grid(row=5, column=1)
- label6_1 = tk.Label(win, text = "min = ")
- label6_1.grid(row=5, column=2)
- label6_2 = tk.Label(win, text = "max = ")
- label6_2.grid(row=5, column=3)
- # Ph direction resolution
- label7 = tk.Label(win, text = 'Phase resolution')
- label7.grid(row=6, column=0,sticky = "E")
- textBox7 = tk.Entry(width = 6)
- textBox7.insert(0, Np)
- textBox7.grid(row=6, column=1)
- label7_1 = tk.Label(win, text = "min = ")
- label7_1.grid(row=6, column=2)
- label7_2 = tk.Label(win, text = "max = ")
- label7_2.grid(row=6, column=3)
- # Pixel BW
- label8 = tk.Label(win, text = 'Pixel BW, Hz')
- label8.grid(row=7, column=0,sticky = "E")
- textBox8 = tk.Entry(width = 6)
- textBox8.insert(0, BW_pixel)
- textBox8.grid(row=7, column=1)
- label8_1 = tk.Label(win, text = "min = ")
- label8_1.grid(row=7, column=2)
- label8_2 = tk.Label(win, text = "max = ")
- label8_2.grid(row=7, column=3)
- # TE, s
- label9 = tk.Label(win, text = 'N_TE')
- label9.grid(row=8, column=0,sticky = "E")
- textBox9 = tk.Entry(width = 6)
- textBox9.insert(0, N_TE)
- textBox9.grid(row=8, column=1)
- label9_1 = tk.Label(win, text = "min = ")
- label9_1.grid(row=8, column=2)
- label9_2 = tk.Label(win, text = "max = ")
- label9_2.grid(row=8, column=3)
- # TR, s
- label10 = tk.Label(win, text = 'TR, s')
- label10.grid(row=9, column=0,sticky = "E")
- textBox10 = tk.Entry(width = 6)
- textBox10.insert(0, TR)
- textBox10.grid(row=9, column=1)
- label10_1 = tk.Label(win, text = "min = ")
- label10_1.grid(row=9, column=2)
- label10_2 = tk.Label(win, text = "max = ")
- label10_2.grid(row=9, column=3)
- # Flip angle, degree
- label11= tk.Label(win, text = 'Flip angle, degree')
- label11.grid(row=10, column=0,sticky = "E")
- textBox11 = tk.Entry(width = 6)
- textBox11.insert(0, alpha)
- textBox11.grid(row=10, column=1)
- angle_min, angle_max = 1, 90
- label11_1 = tk.Label(win, text = "min = "+ str(angle_min))
- label11_1.grid(row=10, column=2)
- label11_2 = tk.Label(win, text = "max = "+ str(angle_max))
- label11_2.grid(row=10, column=3)
- # Refocusing angle, degree
- label12= tk.Label(win, text = 'Refocusing angle, degree')
- label12.grid(row=11, column=0,sticky = "E")
- textBox12 = tk.Entry(width = 6)
- textBox12.insert(0, beta)
- textBox12.grid(row=11, column=1)
- beta_min, beta_max = 110, 180
- label12_1 = tk.Label(win, text = "min = "+ str(beta_min))
- label12_1.grid(row=11, column=2)
- label12_2 = tk.Label(win, text = "max = "+ str(beta_max))
- label12_2.grid(row=11, column=3)
- # Concatenations
- label13= tk.Label(win, text = 'Concatenations')
- label13.grid(row=12, column=0,sticky = "E")
- textBox13 = tk.Entry(width = 6)
- textBox13.insert(0, Conct)
- textBox13.grid(row=12, column=1)
- label13_1 = tk.Label(win, text = "min = ")
- label13_1.grid(row=12, column=2)
- label13_2 = tk.Label(win, text = "max = ")
- label13_2.grid(row=12, column=3)
- # ETL
- label14= tk.Label(win, text = 'ETL')
- label14.grid(row=13, column=0,sticky = "E")
- textBox14 = tk.Entry(width = 6)
- textBox14.insert(0, ETL)
- textBox14.grid(row=13, column=1)
- label14_1 = tk.Label(win, text = "min = ")
- label14_1.grid(row=13, column=2)
- label14_2 = tk.Label(win, text = "max = ")
- label14_2.grid(row=13, column=3)
- # Averages
- label15= tk.Label(win, text = 'Averages')
- label15.grid(row=14, column=0,sticky = "E")
- textBox15 = tk.Entry(width = 6)
- textBox15.insert(0, Average)
- textBox15.grid(row=14, column=1)
- label15_1 = tk.Label(win, text = "min = ")
- label15_1.grid(row=14, column=2)
- label15_2 = tk.Label(win, text = "max = ")
- label15_2.grid(row=14, column=3)
- # phase oversampling
- label16= tk.Label(win, text = 'Ph oversampling,%')
- label16.grid(row=15, column=0,sticky = "E")
- textBox16 = tk.Entry(width = 6)
- textBox16.insert(0, ph_over)
- textBox16.grid(row=15, column=1)
- label16_1 = tk.Label(win, text = "min = ")
- label16_1.grid(row=15, column=2)
- label16_2 = tk.Label(win, text = "max = ")
- label16_2.grid(row=15, column=3)
- set_limits()
- btn1 = tk.Button(win, text = 'Set', command = set_limits)
- btn1.grid(row=16, column=0,sticky = "E")
- btn1 = tk.Button(win, text = 'Save', command = save_param)
- btn1.grid(row=16, column=3)
- btn1 = tk.Button(win, text = 'Read json', command = read_json)
- btn1.grid(row=17, column=0,sticky = "E")
- # filename
- label17= tk.Label(win, text = 'File name')
- label17.grid(row=18, column=0,sticky = "E")
- textBox17 = tk.Entry(width = 27)
- textBox17.insert(0, "TSE")
- textBox17.grid(row=18, column=1, columnspan=3,sticky = "W")
- win.mainloop()
|