CINRAD數(shù)據(jù)處理

繼續(xù)雷達(dá)拼圖的工作
本文主要介紹如何將CINRAD雷達(dá)數(shù)據(jù)轉(zhuǎn)換為pyart所需要的格式磕潮。

經(jīng)過查看pyart官方文檔弛矛,可以發(fā)現(xiàn)佣耐,盡管pyart能夠讀取多種雷達(dá)數(shù)據(jù)格式矫夯,但對我國的新一代天氣雷達(dá)基本是無效的,因此需要對pyart進(jìn)行有益的補(bǔ)充,也就是將我們的CINrAD讀取方式加入测蘑。
好了灌危,到此整理一下思路,分以下步驟:

  • 查看pyart的格式以及其讀取其他雷達(dá)數(shù)據(jù)的方法
  • 查看CINRAD基數(shù)據(jù)的讀取方式
  • 綜合前兩步驟碳胳,寫出讀取讀取代碼

查看pyart的格式

隨便打開一個(gè)pyart的例子勇蝙,我們可以發(fā)現(xiàn)pyart將讀取模塊命名為io,我們找一下和雷達(dá)拼圖有關(guān)的代碼

XSAPR_SW_FILE = 'swx_20120520_0641.nc'
XSAPR_SE_FILE = 'sex_20120520_0641.nc'
radar_sw = pyart.io.read_cfradial(XSAPR_SW_FILE)
radar_se = pyart.io.read_cfradial(XSAPR_SE_FILE)

可以發(fā)現(xiàn)在io中有個(gè)read_cfradial函數(shù)挨约,于是順藤摸瓜找到這個(gè)read_cfradial函數(shù)存在于io文件夾下的cfradial.py文件味混。

進(jìn)入下一步,打開文件诫惭,找到read_cfradial函數(shù)翁锡。那就慢慢的看吧,于是又發(fā)現(xiàn)這個(gè)函數(shù)的大部分內(nèi)容都是沒用的夕土,因?yàn)樗拇蟛糠謨?nèi)容都是對雷達(dá)數(shù)據(jù)進(jìn)行整理的馆衔,我們不關(guān)心,因?yàn)镃INRAD的格式和它肯定不同怨绣,因此不需要參考哈踱。

最終看一下這個(gè)函數(shù)返回的結(jié)果吧,見下圖:


image.png

我們發(fā)現(xiàn):

  • 返回了一個(gè)Radar
  • 給類傳遞了很多參數(shù)(暫時(shí)不理會)

到這里就大概有了了解梨熙,這個(gè)讀取的方式也并不是那么的復(fù)雜,不過就是將數(shù)據(jù)讀取進(jìn)來進(jìn)行整理并返回pyart的Radar類刀诬。那如何了解該函數(shù)是如何整理的呢咽扇?

另辟蹊徑的方式就是根據(jù)pyart給出的例子,看看它讀取出來的數(shù)據(jù)具有什么樣的結(jié)構(gòu)不就可以了陕壹?結(jié)論如下:

  • time,range,metadata,scan_type.....,除scan_type是字符串外质欲,其他參數(shù)都是字典
  • 每個(gè)參數(shù)的字典結(jié)構(gòu)類似netcdf格式,其中data鍵是數(shù)據(jù)糠馆,其他鍵類似netcdf格式中的attribute嘶伟。
  • 數(shù)據(jù)的組成形式,pyart很有意思又碌,它儲存數(shù)據(jù)的方式比較特殊九昧,我們知道雷達(dá)是一條射線一樣發(fā)出輻射的,每次接收回來的數(shù)據(jù)也是一條毕匀。那么它就將這些所有的射線數(shù)據(jù)存儲為二維數(shù)組铸鹰,第一維對應(yīng)于每條射線,第二維對應(yīng)于每條射線上的數(shù)據(jù)皂岔。

當(dāng)然這里說pyart很有意思是在沒有查看CINRAD數(shù)據(jù)格式之前蹋笼。

到這里關(guān)于pyart數(shù)據(jù)結(jié)構(gòu)部分就完事了。下面來看一下CINRAD基數(shù)據(jù)讀取。

CINRAD

寫在最前的參考內(nèi)容
IDL讀取CC雷達(dá)
Python 新一代多普勒天氣雷達(dá)基數(shù)據(jù)可視化
PyCINRAD
CINRAD雷達(dá)產(chǎn)品顯示系統(tǒng)

第一步當(dāng)然是度娘一下CINRAD相關(guān)內(nèi)容剖毯,了解它的數(shù)據(jù)格式等等圾笨,終于讓我發(fā)現(xiàn)了一下可供參考的信息。


image.png

image.png

image.png

終于看到雷達(dá)基數(shù)據(jù)格式了逊谋,我們已經(jīng)知道基數(shù)據(jù)以二進(jìn)制的方式保存擂达,使用python讀取基數(shù)據(jù)是非常方便的,類似于處理Grads數(shù)據(jù)涣狗。直接使用np.fromfile即可谍婉,當(dāng)然也可以采用如下方式:

with open(file, 'rb') as fopen:
    data = np.fromfile(fopen.read(), dtype=***, count=1)

這里的dtype也就是對應(yīng)于基數(shù)據(jù)格式,count則是讀取幾次dtype镀钓,如果為-1則全部讀取穗熬。
對于上圖的基數(shù)據(jù),我們很容易就可以寫出它的dtype:

header_dtype = np.dtype(
    [
    ('header1', 'u2', 7),
    (radar_data', 'u2'),
    ('header2', 'u2', 6),
    ...
    ]
    )

當(dāng)然這里也搜索到一些C#的代碼供參考丁溅,可以將其轉(zhuǎn)換為對應(yīng)的python中numpy庫能夠識別的數(shù)據(jù)類型即可唤蔗。

后來進(jìn)一步的搜索發(fā)現(xiàn)已經(jīng)有國內(nèi)的小伙伴完成了部分工作,并且做得非常不錯窟赏,例如PyCINRADPython 新一代多普勒天氣雷達(dá)基數(shù)據(jù)可視化妓柜,既然這樣那就可以參考他們的工作來完成相關(guān)的基數(shù)據(jù)讀取部分了。根據(jù)薛志遠(yuǎn)的描述涯穷,雷達(dá)數(shù)據(jù)每個(gè)方位角(射線)的數(shù)據(jù)都包括了文件頭基數(shù)據(jù)部分保留字段棍掐。

image.png

讀取的具體代碼就不在這里貼出來了。
最終經(jīng)過整理拷况,將SA/SB型號的雷達(dá)基數(shù)據(jù)轉(zhuǎn)換為pyart的Radar類的代碼如下:

import os
import re
import datetime
import pyart
from pyart.core import Radar


def __check_base_type(self, filein):
        # Z9517_BASE_SA_20190531_082100.bin
        # D:\Download\radar\Z_RADR_I_Z9010_20190521010000_O_DOR_SA_CAP.bin.bz2
        type_re_math = '_SA_|_SB_|_SC_|_CA_|_CB_|_CC_|_CD_'
        station_re_math = '_Z\d{4}_|Z\d{4}_|_Z\d{4}'
        time_re_math = '_\d{14}_|_\d{8}_|_\d{6}'
        filename = os.path.basename(filein)
        tmp_search = re.findall(type_re_math, filename)
        if tmp_search and len(tmp_search) == 1:
            radar_base_data_type = tmp_search[0].replace('_', '')
        tmp_search = re.findall(station_re_math, filename)
        if tmp_search and len(tmp_search) == 1:
            radar_station_id = tmp_search[0].replace('_', '')
        tmp_search = re.findall(time_re_math, filename)
        if tmp_search:
            if len(tmp_search) == 1:
                radar_scan_time_str = tmp_search[0].replace('_', '')
                radar_scan_time = datetime.datetime.strptime(
                    radar_scan_time_str, '%Y%m%d%H%M%S'
                    ) + datetime.timedelta(hours=8)
            elif len(tmp_search) == 2:
                radar_scan_time_str = ''.join([i.replace('_', '') for i in tmp_search])
                radar_scan_time = datetime.datetime.strptime(
                    radar_scan_time_str, '%Y%m%d%H%M%S'
                    ) 
        return radar_station_id, radar_base_data_type, radar_scan_time

def __prepare_work(self, filein):
        radar_station_id, radar_base_data_type, radar_scan_time = self.__check_base_type(filein)
        radar_station_info = self.stainfo.get(radar_station_id)
        if radar_station_info is not None:
            radar_station_name = radar_station_info[0]
            radar_longitude = radar_station_info[1]
            radar_latitude = radar_station_info[2]
            radar_altitude = radar_station_info[-1]
        else:
            radar_station_name = radar_station_info[0]
            radar_longitude = radar_station_info[1]
            radar_latitude = radar_station_info[2]
            radar_altitude = radar_station_info[-1]

        data_prepare = {
            'time': dict(
                comment='time ozone is BEIJING!',
                calendar = 'standard',
                units = 'seconds since 1970-01-01 00:00',
                standard_name='time',
                long_name='time in seconds since volume start',
                data=[date2num(radar_scan_time,'seconds since 1970-01-01 00:00','standard')]),
            'radar_lon' : dict(
                units = 'degrees_east',
                standard_name = 'Longitude',
                data=np.ma.array([radar_longitude], mask=0)
                ),
            'radar_lat':dict(
                units = 'degrees_north',
                standard_name = 'Latitude',
                data = np.ma.array([radar_latitude], mask=0)
                ),
            'radar_alt':dict(
                units='meters',
                standard_name = 'Altitude',
                data=np.ma.array([radar_altitude], mask=0)
                ),
            'metadata':dict(
                radar_station_name = radar_station_name,
                radar_station_id = radar_station_id,
                radar_base_data_type = radar_base_data_type,
                radar_scan_time = radar_scan_time,
                radar_longitude = radar_longitude,
                radar_latitude = radar_latitude,
                radar_altitude = radar_altitude,
                )
              }
        return data_prepare

def __radar_SA_SB(self, filein):
        data_prepare = self.__prepare_work(filein)
        with self.__return_file_object(filein) as fopen:
            data = np.frombuffer(fopen.read(), dtype=self.SAB_dtype)
        scan_type = 'ppi'
        data_prepare['time']['data'] = data_prepare['time']['data'] * data['azimuth'].size
        azimuth = dict(
            data = (data['azimuth'] / 8.) * (180/4096),
            units = 'degrees',
            standard_name='beam_azimuth_angle',
            long_name='azimuth_angle_from_true_north'
            )
        elevation = dict(
            units='degrees',
            comment='Elevation of antenna relative to the horizontal plane',
            standard_name='beam_elevation_angle',
            long_name='elevation_angle_from_horizontal_plane',
            data=(data['elevation'] / 8.) * (180/4096)
            )

        metadata_new = dict(
            nyquist_vel = data['nyquist_vel'][0]/100,
            instrument_name='China CINRAD'
            )
        data_prepare['metadata'].update(metadata_new)

        _range=dict(
            units='meters',
            meters_between_gates=50,
            long_name='range_to_measurement_volume',
            standard_name='projection_range_coordinate',
            meters_to_center_of_first_gate= 0,
            data = np.ma.array(np.arange(0,460,1), mask=False)
            )

        sweep_start_ray_index = dict(
            data=np.ma.array(np.where(np.logical_or(data['radial_state']==3, data['radial_state']==0))[0], mask=False),
            units='count',
            long_name='index of first ray in sweep, 0-based',
            )
        sweep_end_ray_index = dict(
            data=np.ma.array(np.where(np.logical_or(data['radial_state']==4, data['radial_state']==2))[0], mask=False),
            units='count',
            long_name='index of last ray in sweep, 0-based',
            )

        fix_angle = dict(
            units='degrees',
            long_name='target_angle_for_sweep',
            standard_name = 'target_fixed_angle',
            data=np.ma.array(np.unique(elevation['data']), mask=False)
            )

        sweep_number = dict(
            units='count', 
            long_name='sweep_number',
            data=np.ma.array(np.arange(len(sweep_start_ray_index['data'])), mask=False)
            )

        sweep_mode = dict(
            units='uniteless',
            long_name='sweep_mode',
            comment= 'Options are:"sector","coplane",rhi","vertical_pointing","idle","azimuth_surveillance","elevation_surveillance","sunscan","pointing","manual_ppi","manual_rhi"',
            data = np.ma.array(['manual_ppi'] * len(sweep_start_ray_index['data']), mask=False)
            )

        fields = dict(
            reflectivity_horizontal=dict(
                _FillValue=0,
                units='dBZ',
                long_name='equivalent_reflectivity_factor',
                standard_name='equivalent_reflectivity_factor',
                valid_max=90,
                valid_min=0,
                data=(np.ma.masked_where(data['r']==0, data['r']) -2 ) /2 -32
                )
            )


        return Radar(
            data_prepare['time'], _range,
            fields, data_prepare['metadata'], scan_type,
            data_prepare['radar_lat'],  data_prepare['radar_lon'], data_prepare['radar_alt'],
            sweep_number, sweep_mode, fix_angle, sweep_start_ray_index, sweep_end_ray_index,
            azimuth, elevation
            )

上述代碼沒有經(jīng)過深度整理作煌,比較粗糙,未來經(jīng)過整理也會在Github上開源赚瘦。
下面對結(jié)果進(jìn)行檢驗(yàn)粟誓,通過繪制ppi來檢驗(yàn)。

fig = plt.figure(figsize=[8, 8])
display = pyart.graph.RadarMapDisplay(radar)
display.plot_ppi_map('reflectivity_horizontal', sweep=0, resolution='50m',
                         vmin=-8, vmax=64, 
                         projection=ccrs.PlateCarree(), cmap='pyart_LangRainbow12')
plt.savefig('../test.png')
test.png

image.png

二者對比相差不大

結(jié)論

完成了SA/SB型號雷達(dá)起意,下面的雷達(dá)型號會比較的快了鹰服。加油!揽咕!
于2019年6月10日23:42:41

同理對于CA/CB雷達(dá)僅僅是雷達(dá)基數(shù)據(jù)格式的不同悲酷,其他處理方式都是相同的。
于2019年6月10日23:59:07

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亲善,一起剝皮案震驚了整個(gè)濱河市舔涎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逗爹,老刑警劉巖亡嫌,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚎于,死亡現(xiàn)場離奇詭異,居然都是意外死亡挟冠,警方通過查閱死者的電腦和手機(jī)于购,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來知染,“玉大人肋僧,你說我怎么就攤上這事】氐” “怎么了嫌吠?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掺炭。 經(jīng)常有香客問我辫诅,道長,這世上最難降的妖魔是什么涧狮? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任炕矮,我火速辦了婚禮,結(jié)果婚禮上者冤,老公的妹妹穿的比我還像新娘肤视。我一直安慰自己,他們只是感情好涉枫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布邢滑。 她就那樣靜靜地躺著,像睡著了一般愿汰。 火紅的嫁衣襯著肌膚如雪困后。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天尼桶,我揣著相機(jī)與錄音,去河邊找鬼锯仪。 笑死泵督,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庶喜。 我是一名探鬼主播小腊,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼久窟!你這毒婦竟也來了秩冈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤斥扛,失蹤者是張志新(化名)和其女友劉穎入问,沒想到半個(gè)月后丹锹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芬失,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年楣黍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棱烂。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡租漂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颊糜,到底是詐尸還是另有隱情哩治,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布衬鱼,位于F島的核電站业筏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馁启。R本人自食惡果不足惜驾孔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惯疙。 院中可真熱鬧翠勉,春花似錦、人聲如沸霉颠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒿偎。三九已至朽们,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诉位,已是汗流浹背骑脱。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苍糠,地道東北人叁丧。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像岳瞭,于是被迫代替她去往敵國和親拥娄。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容