#!/usr/bin/env python # coding: utf-8 # ## 5. Állandó fázisú pont módszere, SPPMethod # # Ez a módszer alapjaiban kissé különbözik a többitől. Az előzőleg leírt globális metódusok, mint domain átváltás, kivágás, stb. itt is működnek, de másképpen kell kezelni őket. *Megjegyezném, hogy mivel ez a módszer interaktív elemet tartalmaz, az egyelőre csak Jupyter Notebook-ban stabil.* # In[1]: import numpy as np import matplotlib.pyplot as plt import pysprint as ps # Példaként a korábban már bemutatott `ps.Generator` segítségével generálni fogok egy sorozat interferogramot, majd azon bemutatom a kiértékelés menetét. Valós méréseknél teljesen hasonlóképpen végezhető a kiértékelés. A legegyszerűbb módszer, hogy különböző karok közti időbeli késleltetésnél generáljunk és elmentsük azokat az alábbi cellában látható. A megkülönböztethetőség miatt minden fájlt a hozzá tartozó karok közti időbeli késleltetésnek megfelelően nevezem el. # In[2]: for delay in range(-200, 201, 50): g = ps.Generator(1, 3, 2, delay, GDD=400, TOD=-500, normalize=True) g.generate_freq() np.savetxt(f'{delay}.txt', np.transpose([g.x, g.y]), delimiter=',') # A kód lefuttatásával a munkafüzet környtárában megjelent 7 új txt fájl. # # Ehhez a kiértékelési módszerhez először fel kell építeni egy listát a felhasználandó interferogramok fájlneveivel. Ezt manuálisan is megtehetjük, itt ezt elkerülve egy rövidítést fogok használni. # In[3]: ifg_files = [f"{delay}.txt" for delay in range(-200, 201, 50)] # In[4]: print(ifg_files) # Ha nem hasonló sémára épülnek a felhasználandó fájlok nevei, akkor természetesen a fenti trükk nem működik és egyenként kell beírnunk őket. Miután definiáltuk a fájlneveket a következő lépés a # ```python # ps.SPPMethod(ifg_names, sam_names=None, ref_names=None, **kwargs) # ``` # meghívása: # In[5]: myspp = ps.SPPMethod(ifg_files, decimal=".", sep=",", skiprows=0, meta_len=0) # A `**kwargs` keyword argumentumok itt elfogadják a korábban már bemutatott `parse_raw` funkció argumentumait (a kódban belül azt is hívja meg egyesével minden interferogramon), hiszen a fájlok sémáját itt is fontos megadni a helyes betöltéshez. A tárgy- és referencianyaláb spektrumai természetesen opcionális argumentumok, mi dönthetjük el, hogy normáljuk-e az interferogramokat. # # Az `SPPMethod` objektum először ellenőrzi, hogy a listában lévő fájlnevek valóban léteznek-e, és ha nem, akkor hibával tér vissza. Az `SPPMethod`-nak vannak további metódusai, ilyen pl. a `len(..)`, vagy az `SPPMethod.info`. Az első visszaadja, hogy hány interferogram van jelenleg az objektumban (ez jelen esetben 9), a második pedig a kiértékelés során mutatja majd, hogy hány interferogramból rögzítettünk információt (ez jelenleg 0/9). Később talán `append` (ilyen már van a 0.12.5 verziótól), `insert` és `delete` metódusokat is beépítek. # In[6]: print(len(myspp)) # In[7]: print(myspp.info) # Az SPPMethod objektum listaszerűen viselkedik: lehet indexelni is. Mivel benne 9 darab interferogram van, ezért egy ilyen indexelés egy `ps.Dataset` objektumot ad vissza. Ez az alapja minden kiértékelési módszernek, így ez ismeri a korábban bemutatott metódusokat. Tegyük fel, hogy a 3. interferogram adatait ki szeretnénk iratni, és szeretnénk megkapni az y értékeit `np.ndarray`-ként. Ekkor a 2 indexet használva (mivel itt is 0-tól indul a számozás): # In[8]: # a harmadik interferogram adatainak kiíratása print(myspp[2]) # In[9]: # a harmadik interferogram y értékeinek kinyerése, mint np.array y_ertekek = myspp[2].data.y.values print(y_ertekek) print(type(y_ertekek)) # Újra hangsúlyozom, minden eddig bemutatott metódus ezeken a kvázi listaelemeken is működik, köztük a `chdomain`, vagy `slice` is. Ezt használjuk ki a kiértékeléshez egy *for* ciklusban. A kiértékeléshez a definiált `SPPMethod`-on meg kell hívni egy for ciklust. Ez végigfut a benne lévő összes interferogramon. Azt, hogy mit akarunk csinálni adott interferogrammal, azt a cikluson belül tudjuk megadni. Az alapvető séma a következő: #
# for ifg in myspp:
#     - előfeldolgozása az adott interferogramnak
#     - az interaktív SPP Panel megnyitása és adatok rögzítése
#     
# - a calculate metódus meghívása a cikluson kívül(!)
# 
# # Ez kód formájában az alábbi cellában látható. Itt külön jelöltem, hogy melyik rész meddig tart. # In[10]: # az interaktív számításokat fontos a with blokkon belülre írni with ps.interactive(): for ifg in myspp: # -----------------------------------Előfeldolgozás----------------------------------------- # Ha valós mérésünk van, érdemes valamilyen módon kiíratni a kommentet, # ami az interferogram fájlban van, hogy meg tudjuk állapítani milyen késleltetésnél készült. # Jelen esetben ennek nincs értelme, mivel a szimulált fájlokkal dolgozom. # Ezt legegyszerűbben az alábbi sorral tehetnénk meg: # print(ifg.meta['comment']) # vagy esetleg a teljes metaadatok kiíratása: # print(ifg.meta) # Ha hullámhossztartományban vagyunk, először át kell váltani. # Én frekvenciatartományban szimuláltam, ezért itt kihagyom. Ha szükség van rá a # következő sort kell használni. # ifg.chdomain() # Pl. 1.2 PHz alatti körfrekvenciaértékek kivágása. Mivel nem adtam meg stop értéket, így a felső # határt érintetlenül hagyná, ha futtatnám. Nyilván ez is opcionális. # ifg.slice(start=1.2) # -----------------------------Az interaktív panel megnyitása------------------------------- ifg.open_SPP_panel() # ---------------------------------A ciklus utáni rész------------------------------------------ # A cikluson kívül a save_data metódus meghívása, hogy elmentsük a beírt adatainkat fájlba is. # Ez természetesen opcionális, de annak érdekében, hogy biztosan ne veszítsünk adatot érdemes ezt is elvégezni. myspp.save_data('spp.txt') # a cikluson kívül meghívjuk a calculate függvényt myspp.calculate(reference_point=2, order=3); # A magyarázatok nélkül szimulált esetben az egész kód az alábbi, összesen 8 sorra egyszerűsödik. Valós mérés esetén néhány előfeldolgozási lépés és kiíratás természetesen még hozzáadódhat ehhez. # # ```python # import pysprint as ps # # ifg_files = [f"{delay}.txt" for delay in range(-200, 201, 50)] # # s = ps.SPPMethod(ifg_files, decimal=".", sep=",", skiprows=0, meta_len=0) # # with ps.interactive(): # for ifg in s: # ifg.open_SPP_panel() # # s.save_data('spp.txt') # s.calculate(reference_point=2, order=2, show_graph=True) # ``` # Miután a számolást már elvégezte a program, akkor elérhetővé válik rajta a `GD` property. Ez az illesztett görbét reprezentálja, típusa `ps.core.phase.Phase`. Bővebben erről a `Phase` leírásában. # In[11]: myspp.GD # #### 5.1 Számolás nyers adatokból # Mivel az `spp.txt` fájlba elmentettük az bevitt adatokat, azokból egyszerűen lehet újraszámolni az illesztést. Töltsük be `np.loadtxt` segítségével, majd használjuk a `ps.SPPMethod.calculate_from_raw` függvényt. # In[12]: delay, position = np.loadtxt('spp.txt', delimiter=',', unpack=True) myspp.calculate_from_raw(delay, position, reference_point=2, order=3); # Az előbbi esetben látható, hogy ugyan azt az eredményt kaptuk, mint előzőleg. Ez akkor is hasznos lehet, ha már megvannak a leolvasott SPP pozícióink a hozzá tartozó késleltetésekkel és csak a számolást akarjuk elvégezni. Ekkor még létre sem kell hozni egy új objektumot, csak meghívhatjuk a függvényt következő módon: # # In[13]: # ehhez beírtam egy teljesen véletlenszerű adatsort delay_minta = [-100, 200, 500, 700, 900] position_minta = [2, 2.1, 2.3, 2.45, 2.6] ps.SPPMethod.calculate_from_raw(delay_minta, position_minta, reference_point=2, order=3); # **FONTOS MEGJEGYZÉS:** # # Az `order` argumentum a program során mindig a keresett diszperzió rendjét adja meg. # #### 5.2 Számolás egy további módon # Mivel továbbra is ugyan ezekkel az adatsorokkal és a `myspp` objektummal dolgozom, most törlöm az összes rögzített adatot belőlük. Ehhez a `SPPMethod.flush` függvényt használom. (Valószínűleg ez a felhasználónak kevésszer szükséges, de elérhető.) # In[14]: myspp.flush() # Korábban már észrevehettük, hogy a kiíratás során - legyen bármilyen módszerről is szó - megjelentek olyan sorok is, hogy `Delay value: Not given` és `SPP position(s): Not given`. Például a `myspp` első interferogramja esetén most ez a helyzet: # In[15]: print(myspp[0]) # Ahogyan a `Dataset` leírásában már szerepelt, lehetőségünk van megadni a betöltött interferogramokon az SPP módszerhez szükséges adatokat. Ekkor a `ps.SPPMethod.calculate_from_ifg(ifgs, reference_point, order)` függvénnyel kiértékelhetjük a benne lévő interferogramokat a következő módon: # In[16]: # kicsomagolok öt interferogramot a generált 7 közül elso_ifg = myspp[0] masodik_ifg = myspp[1] harmadik_ifg = myspp[2] negyedik_ifg = myspp[3] otodik_ifg = myspp[4] # In[17]: # beállítok rájuk véletlenszerűen SPP adatokat elso_ifg.delay = 0 elso_ifg.positions = 2 masodik_ifg.delay = 100 masodik_ifg.positions = 2 harmadik_ifg.delay = 150 harmadik_ifg.positions = 1.6 negyedik_ifg.delay = 200 negyedik_ifg.positions = 1.2 otodik_ifg.delay = 250 otodik_ifg.positions = 1, 3, 1.2 # listába teszem őket ifgs = [elso_ifg, masodik_ifg, harmadik_ifg, negyedik_ifg, otodik_ifg] # In[18]: # meghívom a calculate_from_ifg függvényt ps.SPPMethod.calculate_from_ifg(ifgs, reference_point=2, order=3); # Ez úgy lehet hasznos, hogy amikor más módszerrel több interferogramot is kiértékelünk egymás után, csak rögzítjük az SPP adatokat is, aztán a program ezekből egyenként összegyűjti a szükséges információt a kiértékeléshez, majd abból számol. # #### 5.3 Az SPPMethod működéséről mélyebben, cache, callbacks # # # Az `SPPMethod` alapvető működését az adatok rögzítése közben az alábbi ábra mutatja. # # ![SPP működése](spp_diagram.svg) # # A hurok az `SPPMethod`-ból indul, ahol a használandó fájlok neveit, betöltési adatokat, stb. adunk meg. Ezen a ponton még semmilyen számolás és betöltés nem történik. Ezután az `SPPMethod` bármely elemének hívására egy `Dataset` objektum jön létre. Ezen megnyitható az `SPPEditor`, amiben az állandó fázisú pont(ok) helyét és a karok közti késleltetést lehet megadni. Hitelesítés után az SPP-vel kapcsolatos információk az interaktív szerkesztőből visszakerülnek a létrehozott `Dataset` objektumba és ott rögzítődnek. Minden így létrejött `Dataset` objektum kapcsolva van az `SPPMethod`-hoz, amiből felépült, így amikor megváltozik egy SPP-vel kapcsolatos adat, az egyből megváltozik az `SPPMetod`-ban is. A `Registry` gondoskodik arról, hogy minden objektum ami a memóriában van az rögzítődjön, illetve szükség esetén elérhető legyen. # # # **Cache** # # Ha próbálunk elérni egy adott elemet (akár a `for` ciklussal, akár indexelve, vagy egyéb módon), létrejön egy `Dataset` objektum. Ez a `Dataset` objektum miután már egyszer elértük a memóriában marad és megtart minden rajta végrehajtott változtatást, beállítást. Alapértelmezetten *128 db* interferogram marad a memóriában egyszerre, de ez a határ szükség esetén megváltoztatható. Az éppen aktuálisan a memóriában lévő interferogramok száma (az adott `SPPMethod`-hoz tartozó) a kiíratás során a `Interferograms cached` cellában látható. # # **Callbacks** # # A fenti ábrán a ciklus utolsó lépése során (ahol a `Dataset` átadja az SPP-vel kapcsolatos adatait a `SPPMethod`-nak) lehetőség van további ún. *callback* függvények meghívására. Egy ilyen beépített callback függvény a `pysprint.eager_executor`. Ez arra használható, hogy minden egyes SPP-vel kapcsolatos adat rögzítése/változtatása után a program azonnal kiszámolja az éppen meglévő adatokból a diszperziót. A korábbiakhoz teljesen hasonlóan kell eljárnunk, csupán a `callback` argumentumot kell megadnunk kiegészítésként. Itt a kötelező argumentumokon túl megadtam a `logfile` és `verbosity` értékeit is: ez minden lépés során a `"mylog.log"` fájlba el fogja menteni az adott illesztés eredményeit és egyéb információkat, továbbá a `verbosity=1` miatt a rögzített adatsort is. Ezzel akár könnyen nyomon követhető a kiértékelés menete. # In[19]: # a folyamatos kiértékeléshez szükséges callback függvény importálása from pysprint import eager_executor myspp2 = ps.SPPMethod( ifg_files, decimal=".", sep=",", skiprows=0, meta_len=0, callback=eager_executor(reference_point=2, order=3, logfile="mylog.log", verbosity=1) ) # In[20]: myspp2 # Ekkor láthatjuk, hogy az `Eagerly calculating` már `True` értékre változik. # Természetesen a program csak értelmes esetekben fogja elvégezni a számolást (pl. szükséges, hogy az adatpontok száma nagyobb legyen, mint az illesztés rendje ). A teljesség kedvéért megemlítendő, hogy könnyen írható akár saját callback függvény is. Futtassuk le az újonnan létrehozott `myspp2`-n a már megismert *for* ciklust: # In[21]: with ps.interactive(): for ifg in myspp2: ifg.open_SPP_panel() # A fenti cella futtatása közben - miután rögzítettünk elég adatot - megjelentek az eredmények, és minden új adatpont hozzáadása esetén frissültek is. Az adatok rögzítését itt ugyan az interaktív felületet használva végeztem, de akár kódban is megtehető: a `myspp` elemein kell az `delay` és `positions` argumentumokat beállítani, és minden új adat hozzáadásánál újra fogja számolni a program. Az előző számolásom közben készült logfile a következő: # In[22]: get_ipython().system('type mylog.log')