Compare commits

..
10 Commits
Author SHA1 Message Date
KenanZhu d5b434cf1a none 2025-04-19 12:37:05 +08:00
KenanZhu 6c999b7dc2 Modify readme 2025-04-19 12:21:54 +08:00
KenanZhu111 a00aeda756 Modify project file structure 2025-01-09 14:13:24 +08:00
KenanZhu111 26e95e7e39 Fix some bug & Optimze the performance 2024-11-18 20:55:28 +08:00
Kenan Zhu b21fa5f4d0 Update ReadmeCN.md 2024-11-17 19:01:53 +08:00
Kenan Zhu 2c9f375bde Update Readme.md 2024-11-17 19:01:24 +08:00
Kenan Zhu 1847ad6dc9 Update Readme.md 2024-11-17 18:53:47 +08:00
Kenan Zhu 179e915e1a Update Readme.md 2024-11-17 17:10:34 +08:00
Kenan Zhu 820f081974 Update ReadmeCN.md 2024-11-17 17:06:47 +08:00
KenanZhu111 357010c137 Fully support the conversion of RINEX 2.xx & RINEX 3.xx 2024-11-17 15:16:42 +08:00
8 changed files with 1000 additions and 670 deletions

No files matched your search

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

+75
View File
@@ -0,0 +1,75 @@
[English](READMEEN.md) | [中文](README.md)
### USING METHOD
Open the English version"KNZ_Convert_en_us_ver_1.2.5.exe", Click the "Single Convert", and
select the path of origin file. Click the button on the right of "Convert" button to choose
the origin format and target format. The converted file will generate in the same diectory
of origin file, if not select the path of output file. Click the "Batch Convert", same
as the operate of "Single Convert", it is to be noticed that the major version of files must
be same or the file of different version will fail to convert. The file name format of
converted is like: COV-{$originfilename}.{$originfileextensions}
### BRIEF
This software is developed by Python, mainly to complish the convert of different format
of RINEX observation data. At present, the convert between RINEX 2.xx and RINEX 3.xx is
already be basiclly supported.
Supported file format:
| FILE TYPE | MAJOR VERSION |2.10|2.11|2.12|3.00|3.01|3.02|3.03|3.04|3.20|3.30|4.00|4.01|
|-----------|---------------|----|----|----|----|----|----|----|----|----|----|----|----|
| RINEX OBS | 2.xx | √ | √ | √ | √ | √ | √ | √ | √ | —— | —— | x | x |
| | 3.xx | √ | √ | √ | √ | √ | √ | √ | √ | —— | —— | x | x |
| | 4.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| RINEX NAV | 2.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| | 3.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| | 4.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| RTCM | 2.xx | —— | —— | —— | —— | —— | —— | —— | —— | x | x | —— | —— |
| | 3.xx | —— | —— | —— | —— | —— | —— | —— | —— | x | x | —— | —— |
The double-character observation type of RINEX 2.xx will be matched by the list
below:
|SYSTEM |FREQ.BAND |FREQUENCY |2.xx CODE | > |3.xx CODE |
|--------|-----------|-----------|-----------------|-----|-------------------|
|GPS |L1 |1575.42 |[C, L, D, S] 1,P1| > |[C, L, D, S] 1C,C1W|
| |L2 |1227.60 |[C, L, D, S] 2,P2| > |[C, L, D, S] 2C,C2W|
| |L5 |1176.45 |[C, L, D, S] 5 | > |[C, L, D, S] 5I |
|GLONASS |G1 |1602+K*9/16|[C, L, D, S] 1,P1| > |[C, L, D, S] 1C,C1P|
| |G2 |1246+K*7/16|[C, L, D, S] 2,P2| > |[C, L, D, S] 2C,C2P|
|Galileo |E2-L1-E1 |1575.42 |[C, L, D, S] 1 | > |[C, L, D, S] 1C |
| |E5a |1176.45 |[C, L, D, S] 5 | > |[C, L, D, S] 5I |
| |E5b |1207.140 |[C, L, D, S] 7 | > |[C, L, D, S] 7I |
| |E5a+b |1191.795 |[C, L, D, S] 8 | > |[C, L, D, S] 8I |
| |E6 |1278.75 |[C, L, D, S] 6 | > |[C, L, D, S] 6C |
|SBAS |L1 |1575.42 |[C, L, D, S] 1 | > |[C, L, D, S] 1C |
| |L2 |1176.45 |[C, L, D, S] 5 | > |[C, L, D, S] 5I |
Because of the observation type of RINEX 3.xx is not completely compatible with the type of
RINEX 2.xx. So, it will be omitted for the observation of same type from the same frequency.
In general, based on the rule: "Convert the first, omit the next." to compatible with the
RINEX 2.xx. At the same time, since this conversion is an irreversible processing method,
the converted observation file data can only be used as a reference when performing this
conversion, cannot replace the original version format.
Tip: if need to use RINEX file of strict format, you are supposed to be not use this software.
### UPDATE STAMP
2024/11/14: Currently, full support for RINEX 2.xx format conversion has been implemented, and
the format output errors of the previous version have been corrected. Currently,
support for converting RINEX 2.xx to RINEX 3.xx is available.
2024/11/15: Corrected output format, ignored RINEX 2.xx free signals and changed matching rules.
2024/11/17: Supports conversion between RINEX2.xx format and RINEX3.xx format, and also supports
conversion between the same major versions.
2024/11/18: Correct crash when input RINEX file with comment in the middle of data; Enhance the
speed of converting.
### UPDATE PLAN
1. ~~Short-term goals:Supports conversion of RINEX2.xx and RINEX3.xx observation files.~~ √
2. Middle-term goals:Supports conversion of RINEX2.xx and RINEX3.xx broadcast ephemeris files.
3. Middle-term goals:Gradually introduce support for conversion of RTCM format files.
4. Long-term goals:Receiver raw data conversion.
+54 -23
View File
@@ -1,34 +1,65 @@
- [中文](ReadmeCN.md) [English](READMEEN.md) | [中文](README.md)
### Using method ### ### 使用方法
打开中文版本“KNZ_Convert_zh_cn_ver_1.2.5.exe”,点击单文件处理,选择原始文件路径。点击转换右侧按钮
选择原始格式和目标格式。如果未指定输出文件路径,转换后的文件将生成在原始文件的同一目录下。点击批文件
处理,与单文件转换相同操作下,需要注意要保证输入文件的主要版本相同。否则会转换失败。转换后的文件命名
格式为:COV-{原文件名}.{原文件后缀名}
Click to open KNZ_Convert_en_us_ver_1.2.1.exe of english version. ### 软件简介
本软件是基于Python开发,主要实现转换不同版本格式的RINEX观测值文件。目前已经实现RINEX 2.xx格式与
RINEX 3.xx格式基本的相互转换。
Click "Single file convert" or "Batch files convert", input the RINEX file of version of 2.10 or 2.11. 文件格式转换支持列表:
If not click the output diectory, the converted file will generate at the same directory of original file, | FILE TYPE | MAJOR VERSION |2.10|2.11|2.12|3.00|3.01|3.02|3.03|3.04|3.20|3.30|4.00|4.01|
the file name of converted file is "COV-{originalfilename}" |-----------|---------------|----|----|----|----|----|----|----|----|----|----|----|----|
| RINEX OBS | 2.xx | √ | √ | √ | √ | √ | √ | √ | √ | —— | —— | x | x |
| | 3.xx | √ | √ | √ | √ | √ | √ | √ | √ | —— | —— | x | x |
| | 4.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| RINEX NAV | 2.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| | 3.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| | 4.xx | x | x | x | x | x | x | x | x | —— | —— | x | x |
| RTCM | 2.xx | —— | —— | —— | —— | —— | —— | —— | —— | x | x | —— | —— |
| | 3.xx | —— | —— | —— | —— | —— | —— | —— | —— | x | x | —— | —— |
The converted file will note the version of convert at the heder of file.
The converted file own some traits as below: 对于RINEX 2.xx版本的两字符观测值类型将按照如下表格进行匹配:
1. Conversion does not require additional parameter information, |SYSTEM |FREQ.BAND |FREQUENCY |2.xx CODE | > |3.xx CODE |
the file is lossless conversion, only convert the foemat, |--------|-----------|-----------|-----------------|-----|-------------------|
do not modify the information. |GPS |L1 |1575.42 |[C, L, D, S] 1,P1| > |[C, L, D, S] 1C,C1W|
2. The double character observation types of RINEX2.xx version will be automatically matched according to the following table: | |L2 |1227.60 |[C, L, D, S] 2,P2| > |[C, L, D, S] 2C,C2W|
| |L5 |1176.45 |[C, L, D, S] 5 | > |[C, L, D, S] 5I |
|GLONASS |G1 |1602+K*9/16|[C, L, D, S] 1,P1| > |[C, L, D, S] 1C,C1P|
| |G2 |1246+K*7/16|[C, L, D, S] 2,P2| > |[C, L, D, S] 2C,C2P|
|Galileo |E2-L1-E1 |1575.42 |[C, L, D, S] 1 | > |[C, L, D, S] 1C |
| |E5a |1176.45 |[C, L, D, S] 5 | > |[C, L, D, S] 5I |
| |E5b |1207.140 |[C, L, D, S] 7 | > |[C, L, D, S] 7I |
| |E5a+b |1191.795 |[C, L, D, S] 8 | > |[C, L, D, S] 8I |
| |E6 |1278.75 |[C, L, D, S] 6 | > |[C, L, D, S] 6C |
|SBAS |L1 |1575.42 |[C, L, D, S] 1 | > |[C, L, D, S] 1C |
| |L2 |1176.45 |[C, L, D, S] 5 | > |[C, L, D, S] 5I |
[Convert_chart.jpg](Convert_chart.jpg)
### About Project ### 由于RINEX 3.xx版本的观测值类型无法完全兼容RINEX 2.xx版本,所以对于RINEX 3.xx同一频段的同类型观测值
2024/11/14: At present, the RINEX2.xx format conversion has been fully supported, 转换时会进行缺省处理。一般按照原观测值文件头文件中观测值 “先列出先转换,后列出先省略”的方式进行兼容。
the format output error of the previous version has been modified, 同时由于该转换是不可逆的处理方式,因此进行该转换时转换后的观测值文件数据只能作为参考使用,无法替代原生
and the conversion from 2.xx to 3.xx is currently supported. 的版本格式。
2024/11/15: Correct the output format, omit the empty observation and change the 注:如需使用严格的RINEX格式文件,请勿使用本软件。
rule of match.
### Update plan ### ### 更新简记
1. 2.xx and 3.xx versions are fully supported, and support mutual conversion. 2024/11/14: 目前已经对RINEX 2.xx格式转换进行了全面支持,修改了上一版本的格式输出错误,目前支持RINEX 2.xx转换为RINEX 3.xx。
2. Gradually introduce the conversion support for RTCM files.
3. Long-term goal: receiver raw data conversion 2024/11/15: 更正了输出格式,忽略RINEX 2.xx空余信号并更改匹配规则
2024/11/17: 支持RINEX2.xx格式和RINEX3.xx格式的相互转换,同时支持同一主要版本之间的转换。
2024/11/18: 修复了在转换数据中标有注释的RINEX文件时崩溃的错误;提高了文件转换速度。
### 更新计划
1. ~~近期目标:支持转换RINEX2.xx版本与RINEX3.xx版本观测值文件。~~ √
2. 中期目标:支持转换RINEX2.xx版本与RINEX3.xx版本广播星历文件。
3. 中期目标:逐步引入对RTCM格式文件的转换支持。
4. 远期目标:接收机原始数据转换。
-26
View File
@@ -1,26 +0,0 @@
- [English](Readme.md)
### 使用方法 ###
单击打开中文版的 “KNZ_Convert_zh_cn_ver_1.0.6.exe”。
点击“单文件转换”或“批量文件转换”,输入2.10或2.11版本的RINEX文件。
如果没有点击输出目录,转换后的文件将在原文件的同一目录下生成,转换后的文件文件名为“COV-{原始文件名}”。
转换后的文件将在文件头注明convert的版本。
转换后的文件具有以下一些特征:
1. 主要对2.11 & 2.10 格式的RINEX观测值文件进行无损转换,不需要额外参数输入,软件会自动转换格式,不会对原文件信息进行更改。
2. 对于RINEX2.xx版本的两字符观测值类型将按照如下表格进行自动匹配:
[Convert_chart.jpg](Convert_chart.jpg)
### 关于项目 ###
2024/11/14: 目前已经对RINEX2.xx格式转换进行了全面支持,修改了上一版本的格式输出错误,目前支持2.xx转换为3.xx。
2024/11/15: 更正了输出格式,忽略空余信号并更改匹配规则
### 更新计划 ###
1. 2.xx版本与3.xx版本全面支持,支持相互转换。
2. 逐步引入对RTCM格式文件的转换支持。
3. 远期目标:接收机原始数据转换
-555
View File
@@ -1,555 +0,0 @@
# @ File : rnx_convert.py
#
# Copyright (c) 2024 - 2024 KenanZhu. All Right Reserved.
#
# @ Author : KenanZhu
# @ Time : 2024/11/13
# @ Brief :
# :
# @ IDE : PyCharm
#
# ----------------------------------------------------------------------------------------
import re
import time
import math
import threading
emp=''
bnk=' '
# Get local time of converting ----------------------- #
def conv_time():
# generate convert timestamp
covpro='KNZ_Convert ver1.2.5'
covtmp='COV-PGM / DATE'
# Get the locate time and transform to RINEX format
y=time.localtime().tm_year
m=time.localtime().tm_mon
d=time.localtime().tm_mday
h=time.localtime().tm_hour
min=time.localtime().tm_min
sec=time.localtime().tm_sec
localtime=('%04d'%y +
'%02d'%m +
'%02d'%d +bnk+
'%02d'%h +
'%02d'%min+
'%02d'%sec+bnk+
'LLC' +bnk)
outline=(covpro +
20*bnk +
localtime+
covtmp +
'\n')
return outline
# Convert Format ------------------------------ #
def datefmt2_conv(inline):
hou=inline[49:51]
min=inline[52:54]
sec=inline[55:57]
return (inline[0:49] +
hou +
min +
sec +
bnk + "UTC" +
bnk + "PGM / RUN BY / DATE\n")
# Convert Format ------------------------------ #
def datefmt3_conv(inline):
hou=inline[49:51]
min=inline[51:53]
sec=inline[53:55]
return (inline[0:49] +
hou + ':' +
min + ':' +
sec + ':' +
'UTCPGM / RUN BY / DATE\n')
# Convert observ type of RINEX 2.xx to Uni-Format --------------- #
def obsfmt2_conv(obstypes, sys):
counter =0
obstypex=[]
obsbomit=''
match sys:
case 'G':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P': obs='C1W'
else: obs+='C'
elif obs[1:2]=='2':
if obs[0:1]=='P': obs='C2W'
else: obs+='C'
elif obs[1:2]=='5': obs+='I'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
case 'E':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P':
obsbomit+=('%02d'%counter)
continue
else: obs+='C'
elif obs[1:2]=='5': obs+='I'
elif obs[1:2]=='6': obs+='C'
elif obs[1:2]=='7': obs+='I'
elif obs[1:2]=='8': obs+='I'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
case 'R':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P': obs='C1P'
else: obs+='C'
elif obs[1:2]=='2':
if obs[0:1]=='P': obs='C2P'
else: obs+='C'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
case 'S':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P':
obsbomit+=('%02d'%counter)
continue
else: obs+='C'
elif obs[1:2]=='5': obs+='I'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
obstypex.append(obsbomit)
return obstypex
# Convert observ msg of RINEX 2.xx to Uni-Format ---------------- #
def obstype2_conv(inline, in_f, omilist):
# uni-format message label
obsobs='SYS / # / OBS TYPES'
outlines=[]
obstypes=[]
obstypex=[]
system=['G', 'E', 'R', 'S']
obsnum=int(inline[0:6])
omilist.append(obsnum)
for i in range(math.ceil(obsnum/9)):
obstypes.extend(inline[6:60].split())
if i+1==math.ceil(obsnum/9): break
inline = in_f.readline()
for sys in system:
obstypex=obsfmt2_conv(obstypes, sys)
omilist.append(obstypex[-1])
# S___2D_
num =len(obstypex)-1
outline=sys+3*bnk+'%2d'%num+bnk
for i in range(num):
outline+=obstypex[i]+bnk
if (i+1)==num:
bnx=((13-num%13)*4+1)*bnk
outlines.append(outline+bnx+obsobs+'\n')
outline=0*bnk
elif (i+1)%13==0:
outlines.append(outline+bnk+obsobs+'\n')
outline=7*bnk
return outlines
# Convert obstpe msg of RINEX 2.xx to Uni-Format --------------- #
def obstpe2_conv(inline, outlines, desver):
oriver=inline[ 5: 9]
# generate convert stamp
obscov='COV-OBSERVATION DATA'
covpro='COV-RINEX VER / TYPE'
outlines.append( 5*bnk+
oriver+
'->' +
desver+
1*bnk+
obscov+
24*bnk+
covpro+
'\n')
outlines.append(conv_time())
outlines.append(
inline.replace('%s'%re.search(r'[1-9]\d*.\d*|0\.\d*[1-9]\d*', inline).group(), desver))
return
# Convert head_data of RINEX 2.xx to Uni-format -------------- #
def rnx2_head2uni(in_f, desver, outlines, omilist):
# rinex message label
obscom='COMMENT'
obstpe='RINEX VERSION / TYPE'
obsl12='WAVELENGTH FACT L1/2'
obspro='PGM / RUN BY / DATE'
obsobs='# / TYPES OF OBSERV'
obsend='END OF HEADER'
# get input head file & convert
while True:
inline=in_f.readline()
# file is aleady checked so no need to judge again
if not inline: pass
if inline.find(obscom)>=0: outlines.append(inline)
elif inline.find(obspro)>=0: outlines.append(datefmt2_conv(inline))
elif inline.find(obsobs)>=0: outlines.extend(obstype2_conv(inline, in_f, omilist))
elif inline.find(obstpe)>=0: obstpe2_conv(inline, outlines, desver)
elif inline.find(obsl12)>=0: continue
elif inline.find(obsend)>=0: break
else : outlines.append(inline)
# get the end message after break
outlines.append(60*bnk+obsend+'\n')
return 1
# Convert obs of RINEX 2.xx to Uni-Format ------------------- #
def rnx2_data2uni(in_f, outlines, omilist, obsnum):
sPRN=[]
scounter=0
outline=emp
# return false when file end
inline=in_f.readline()
if not inline : return False
# get epoch message & flag state
year = int(inline[0 :3])
month = int(inline[0+3 :3+3])
day = int(inline[0+3+3 :3+3+3])
hour = int(inline[0+3+3+3 :3+3+3+3])
minute= int(inline[0+3+3+3+3 :3+3+3+3+3])
second=float(inline[0+3+3+3+3+3 :3+3+3+3+3+11])
e_flag= int(inline[0+3+3+3+3+3+11 :3+3+3+3+3+11+3])
satnum= int(inline[0+3+3+3+3+3+11+3:3+3+3+3+3+11+3+3])
if not e_flag==0: return True
# time to Uni-Format
outlines.append(
"> %4d %02d %02d %02d %02d %10.7f %2d %2d\n" \
% (year + 2000, month, day, hour, minute, second, e_flag, satnum))
# get list of sPRN
for i in range(math.ceil(satnum/12)):
if not i==0 : inline =in_f.readline()
if i+1< math.ceil(satnum/12): scounter=12
if i+1==math.ceil(satnum/12): scounter=satnum-i*12
for j in range(scounter):
sPRN.append(inline[32+3*j:32+3*(j+1)].replace(bnk,'0'))
# convert epoch data to Uni-Format
for i in range(satnum):
oulist=[]
outline=emp
# get omit list
match sPRN[i][0:1]:
case 'G': omitlis=omilist[0]
case 'E': omitlis=omilist[1]
case 'R': omitlis=omilist[2]
case 'S': omitlis=omilist[3]
# observ amount lower than 5. no need to enter
if obsnum <= 5:
outline=in_f.readline()
outline=inline.replace('\n',emp).replace('\r',emp)
# elif, need to enter
elif obsnum > 5:
for j in range(math.ceil(obsnum/5)):
inline=in_f.readline()
outline+=inline.replace('\n',emp).replace('\r',emp)
if len(outline)%16==15: outline+=bnk
if len(outline)%16==14: outline+=2*bnk
outline+=(5 - int(len(outline)/16))*16*bnk
# omit the empty observ by ''
# outline be divide into parts
for j in range(obsnum):
oulist.append(outline[16*j:16*j+16])
for j in range(len(omitlis)):
oulist[omitlis[j]-1]=emp
# remerge into outline
outline=emp.join(oulist)
outlines.append(sPRN[i]+outline+"\n")
return True
# Convert Uni-format to RINEX 3.xx --------------------- #
def uni2rnx3_data(outlines, out_path):
out_f = open(out_path, 'a')
out_f.writelines(outlines)
out_f.close()
return
# Get observ type need be omitted --------------------- #
def getomit(omilist):
for i in range(len(omilist)):
sysomit=[]
omitlis=omilist[0]
omitnum=len(omitlis)/2
for j in range(int(omitnum)):
sysomit.append(int(omitlis[j*2+0:j*2+2]))
omilist.append(sysomit)
omilist.pop(0)
return
# Convert data of RINEX 2.xx to Des-format ----------------- #
def rnx2des(in_path, out_path, oriver, desver):
headread=False
outlines=[]
omilist=[]
# convert rinex 2.xx(header) to Uni-format
in_f=open(in_path, 'r')
match rnx2_head2uni(in_f, desver, outlines, omilist):
case 1: headread=True
case 0: headread=False
uni2rnx3_data(outlines, out_path)
outlines=[]
if headread:
# get each observ type and omit the empty
obsnum=omilist[0]
omilist.pop(0)
getomit(omilist)
while rnx2_data2uni(in_f, outlines, omilist, obsnum):
match desver[0:1]:
case '2': return 0
case '3': uni2rnx3_data(outlines, out_path)
outlines=[]
return 1
# Reject data of invaild input ----------------------- #
def rnx_reject(in_path, out_path, oriver):
# rinex message label
obstpe='RINEX VERSION / TYPE'
obsmsg='OBSERVATION DATA'
obsend='END OF HEADER'
# reject cause cant input file
try:
with open(in_path, 'r'): pass
except FileNotFoundError: return 0
# reject wrong format or version
in_f=open(in_path, 'r')
while True:
inline=in_f.readline()
if not inline: return 0
if inline.find(obstpe)>=0:
if not inline.find(obsmsg)>=0: return 0
if not inline[ 5: 6]==oriver : return 0
elif inline.find(obsend)>=0: break
# reject cause cant output file
try:
with open(out_path, 'w'): pass
except PermissionError: return 0
return 1
# ---------------------------------------------------------------------------------------
# CONVERT FUNCTION
#
# Brief :Determine the file format and assign handlers
# Return :
# queue msg==0: fail.
# queue msg==1: ok.
# ---------------------------------------------------------------------------------------
def Convert_Un(in_path, out_path, oriver, desver, resqueue):
file_state=False
match rnx_reject(in_path, out_path, oriver):
case 1: file_state=True
case 0: file_state=False
if file_state:
match oriver:
case '2': rnx2des(in_path, out_path, oriver, desver)
case '3': resqueue.put(0)
else : resqueue.put(0)
resqueue.put(1)
return
# Multi-threading task --------------------------- #
def _Convert_Un(in_path, out_path, oriver, desver, resqueue):
T=threading.Thread(
target=lambda :Convert_Un(in_path, out_path, oriver, desver, resqueue)
)
T.start()
T.join()
return resqueue.get()
# --------------------------------------------------------------- #
# The functions below are not be useable. on progress............ #
#
#
# --------------------------------------------------------------- #
# Convert observ type of RINEX 3.xx to Uni-Format --------------- #
def obsfmt_conv_cxx(obstypes):
outlines=[]
outline='%6d'%len(obstypes)
for i in range(len(obstypes)):
outline+='%6s'%obstypes[i]
if (i+1)==len(obstypes):
outlines.append(outline + (9 - (i+1)%9)*6*bnk + '# / TYPES OF OBSERV\n')
outline=''
elif (i+1)%9==0:
outlines.append(outline + '# / TYPES OF OBSERV\n')
outline=6*bnk
return outlines
# Convert observ msg of RINEX 3.xx to Uni-Format ---------------- #
def obstype_conv_cxx(obstypes, inline, in_f):
while True:
if inline[0:1]=='G': flag=True
elif inline[0:1]=='E': flag=True
elif inline[0:1]=='R': flag=True
elif inline[0:1]=='S': flag=True
else: flag=False
if flag:
obsnum=int(inline[2:6])
for i in range(math.ceil(obsnum/13)):
for obs in inline[6:60].split():
if obs[1:2]=='3': continue
if obs[1:2]=='4': continue
if obs[2:3]=='P': obs='P'+obs[1:2]
else: obs=obs[0:2]
if obs and obs not in obstypes:
obstypes.append(obs)
if i+1==math.ceil(obsnum/13):
break
inline=in_f.readline()
return
# Convert head_data of RINEX 3.xx to Uni-format -------------- #
def rnx3_head2uni(in_path, out_path, desver):
try:
with open(in_path, 'r'): pass
except FileNotFoundError:
return -12
obstypes=[]
outlines=[]
in_put=False
output=False
in_f=open(in_path, 'r')
while True:
inline=in_f.readline()
if not inline:
return 0
if in_put and not output:
if not inline.find('SYS / # / OBS TYPES')>=0:
output=True
if in_put and output:
outlines.extend(obsfmt_conv_cxx(obstypes))
in_put=False
output=False
if inline.find('COMMENT')>=0:
outlines.append(inline)
elif inline.find('RINEX VERSION / TYPE')>=0:
if not inline.find('OBSERVATION DATA')>=0:
return -10
if not float(inline[5: 9])>=3:
return -10
# Output convert message
outlines.append(
5*bnk+'%s->%s'%(str(inline[5: 9]),str(desver))+
1*bnk+'COV-OBSERVATION DATA'+
24*bnk+'COV-RINEX VER / TYPE\n')
outlines.append(conv_time())
outlines.append(inline.replace(
'%s'%re.search(r'[1-9]\d*.\d*|0\.\d*[1-9]\d*', inline).group(),
desver))
elif inline.find('PGM / RUN BY / DATE')>=0:
outlines.append(datefmt3_conv(inline))
elif inline.find('SYS / # / OBS TYPES')>=0:
in_put=True
obstype_conv_cxx(obstypes, inline, in_f)
elif inline.find('SYS / PHASE SHIFT')>=0:
continue
elif inline.find('GLONASS COD/PHS/BIS')>=0:
continue
elif inline.find('GLONASS SLOT / FRQ #')>=0:
continue
elif inline.find("END OF HEADER")>=0:
outlines.append(inline)
break
else: outlines.append(inline)
try:
with open(out_path, 'w'): pass
except PermissionError:
return -11
out_f=open(out_path, 'a')
out_f.writelines(outlines)
out_f.close()
return 1
+52 -66
View File
@@ -1,17 +1,17 @@
# @ File : KNZ_Convert.py # @ File : KNZ_Convert.py
#
# @ Brief : GUI main functions.
# #
# Copyright (c) 2024 - 2024 KenanZhu. All Right Reserved. # Copyright (c) 2024 - 2024 KenanZhu. All Right Reserved.
# #
# @ Author : KenanZhu # @ Author : KenanZhu
# @ Time : 2024/11/13 # @ Time stamp : [2024/11/13] First release.
# @ Brief :
# :
# @ IDE : PyCharm # @ IDE : PyCharm
# #
# ---------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------
# ---SELF--- # # ---SELPORT--- #
import rnx_convert as conv import rnx_convert as conv
# ---THRI--- # # ---STDPORT--- #
import queue import queue
import threading import threading
import tkinter as tk import tkinter as tk
@@ -20,17 +20,18 @@ from tkinter import ttk
from PIL import ImageTk from PIL import ImageTk
from base64 import b64decode from base64 import b64decode
from tkinter import filedialog from tkinter import filedialog
# --------------------------------------------------------------------------------------- # ------------- #
# global const
InPaths = [] InPaths = []
# # io oath combox index
I_Box_Counter=0 I_Box_Counter=0
O_Box_Counter=0 O_Box_Counter=0
# # io format list & index
I_Format_Rnx_List=0 I_Format_Rnx_List=0
I_RINEX_BoxList=['2','3'] I_RINEX_BoxList=['2','3']
O_Format_Rnx_List=0 O_Format_Rnx_List=0
O_RINEX_BoxList=['2.10','2.11','2.12','3.00','3.01','3.02','3.03','3.04','3.05'] O_RINEX_BoxList=['2.10','2.11','2.12','3.00','3.01','3.02','3.03','3.04','3.05']
# --------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------- #
def instanceOPT(parenthwnd, func): def instanceOPT(parenthwnd, func):
OPTIONS(parenthwnd, func) OPTIONS(parenthwnd, func)
@@ -143,14 +144,8 @@ class CALLBACK:
return filedir return filedir
path_len -= 1 path_len -= 1
def Convert( def Convert(self,
self, Pro_style, File_O_ynVar, File_I_BoxVar, File_O_BoxVar, Convertcards, Exec_Progress):
Pro_style,
File_O_ynVar,
File_I_BoxVar,
File_O_BoxVar,
Convertcards,
Exec_Progress):
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
# > # >
# Method: # Method:
@@ -168,7 +163,7 @@ class CALLBACK:
'text.Horizontal.TProgressbar', 'text.Horizontal.TProgressbar',
text='Converting...') text='Converting...')
Exec_Progress.config(value=0) Exec_Progress.config(value=0)
if Convertcards.index('current')==0: if Convertcards.index('current')==0:
if not File_I_BoxVar.get(): ErrorMsg+=' path' if not File_I_BoxVar.get(): ErrorMsg+=' path'
else: InPath.append(File_I_BoxVar.get()) else: InPath.append(File_I_BoxVar.get())
elif Convertcards.index('current')==1: elif Convertcards.index('current')==1:
@@ -183,7 +178,7 @@ class CALLBACK:
Exec_Progress.config(value=0) Exec_Progress.config(value=0)
Pro_style.configure('text.Horizontal.TProgressbar', text=ErrorMsg) Pro_style.configure('text.Horizontal.TProgressbar', text=ErrorMsg)
return return
### #
resqueue=queue.Queue() resqueue=queue.Queue()
Pro_style.configure( Pro_style.configure(
'text.Horizontal.TProgressbar', 'text.Horizontal.TProgressbar',
@@ -211,24 +206,11 @@ class CALLBACK:
'text.Horizontal.TProgressbar', 'text.Horizontal.TProgressbar',
text='Complete ! %d successful. %d fails.'%(State_O, len(InPath)-State_O)) text='Complete ! %d successful. %d fails.'%(State_O, len(InPath)-State_O))
def _Convert( def _Convert(self,
self, Pro_style, File_O_ynVar, File_I_BoxVar, File_O_BoxVar, Convertcards, Exec_Progress):
Pro_style,
File_O_ynVar,
File_I_BoxVar,
File_O_BoxVar,
Convertcards,
Exec_Progress):
T=threading.Thread(target=lambda : T=threading.Thread(target=lambda :
self.Convert( self.Convert(Pro_style, File_O_ynVar, File_I_BoxVar, File_O_BoxVar, Convertcards, Exec_Progress))
Pro_style,
File_O_ynVar,
File_I_BoxVar,
File_O_BoxVar,
Convertcards,
Exec_Progress
))
T.start() T.start()
@staticmethod @staticmethod
@@ -294,9 +276,10 @@ class MAINGUI:
# Author: @KenanZhu All Right Reserved. # Author: @KenanZhu All Right Reserved.
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
self.hwnd.title("KNZ_Convert") self.hwnd.title("KNZ_Convert")
self.hwnd.resizable(0, 0)
self.func.Move_center(self.hwnd, 400, 243) self.func.Move_center(self.hwnd, 400, 243)
### self.hwnd.maxsize( 1000, 243)
self.hwnd.minsize( 400, 243)
#
icon_ico = (b'AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAADjsAAA47AAAAAAAAAAAAAAAAAAAAAAAAAAAA' icon_ico = (b'AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAADjsAAA47AAAAAAAAAAAAAAAAAAAAAAAAAAAA'
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
@@ -359,11 +342,11 @@ class MAINGUI:
icon_ico = b64decode(icon_ico) icon_ico = b64decode(icon_ico)
icon_ico = ImageTk.PhotoImage(data=icon_ico) icon_ico = ImageTk.PhotoImage(data=icon_ico)
self.hwnd.tk.call('wm', 'iconphoto', self.hwnd._w, icon_ico) self.hwnd.tk.call('wm', 'iconphoto', self.hwnd._w, icon_ico)
### #
self.initCONVERT() self.initCONVERT()
### #
self.initExecCtrl() self.initExecCtrl()
### #
self.hwnd.protocol('WM_DELETE_WINDOW',self.hwnd.quit) self.hwnd.protocol('WM_DELETE_WINDOW',self.hwnd.quit)
self.hwnd.mainloop() self.hwnd.mainloop()
@@ -394,22 +377,22 @@ class MAINGUI:
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
self.SingleFrame=ttk.Frame(self.hwnd) self.SingleFrame=ttk.Frame(self.hwnd)
### Initialize of input frame # Initialize of input frame
File_I_Frame=tk.LabelFrame( File_I_Frame=tk.LabelFrame(
self.SingleFrame, self.SingleFrame,
text='Object Observation of RINEX') text='Object Observation of RINEX')
File_I_Frame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='0px') File_I_Frame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='0px')
### Initialize of input box # Initialize of input box
File_I_Box=ttk.Combobox( File_I_Box=ttk.Combobox(
File_I_Frame, File_I_Frame,
width=47, width=47,
height=4, height=4,
values=('',), values=('',),
textvariable=self.File_I_BoxVar) textvariable=self.File_I_BoxVar)
File_I_Box.pack(side=tk.LEFT, anchor='w', padx='1px', pady='1px') File_I_Box.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES, anchor='w', padx='1px', pady='1px')
### Initialize of input Button # Initialize of input Button
File_I_Bbutton=ttk.Button( File_I_Bbutton=ttk.Button(
File_I_Frame, File_I_Frame,
text='...', text='...',
@@ -426,17 +409,17 @@ class MAINGUI:
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
self.BatchFrame=ttk.Frame(self.hwnd) self.BatchFrame=ttk.Frame(self.hwnd)
### #
Files_I_Frame=tk.LabelFrame( Files_I_Frame=tk.LabelFrame(
self.BatchFrame, self.BatchFrame,
text='Objects Observation of RINEX') text='Objects Observation of RINEX')
Files_I_Frame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='0px') Files_I_Frame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='0px')
### #
Files_I_CtrlFrame=ttk.Frame(Files_I_Frame) Files_I_CtrlFrame=ttk.Frame(Files_I_Frame)
Files_I_CtrlFrame.pack(side=tk.RIGHT, fill=tk.Y, anchor='e', padx='1px', pady='1px') Files_I_CtrlFrame.pack(side=tk.RIGHT, fill=tk.Y, anchor='e', padx='1px', pady='1px')
### #
Files_I_CtrlInput=ttk.Button( Files_I_CtrlInput=ttk.Button(
Files_I_CtrlFrame, Files_I_CtrlFrame,
text='...', text='...',
@@ -451,23 +434,23 @@ class MAINGUI:
command=lambda :self.func.Clear_I_Files(Files_I_ShowText)) command=lambda :self.func.Clear_I_Files(Files_I_ShowText))
Files_I_CtrlClear.pack(side=tk.BOTTOM, anchor='e', padx='1px', pady='1px') Files_I_CtrlClear.pack(side=tk.BOTTOM, anchor='e', padx='1px', pady='1px')
### #
Files_I_ShowFrame=ttk.Frame(Files_I_Frame) Files_I_ShowFrame=ttk.Frame(Files_I_Frame)
Files_I_ShowFrame.pack(side=tk.LEFT, fill=tk.X, anchor='e', padx='1px', pady='1px') Files_I_ShowFrame.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES, anchor='e', padx='1px', pady='1px')
### #
ScrollbarX=ttk.Scrollbar(Files_I_ShowFrame,orient=tk.HORIZONTAL) ScrollbarX=ttk.Scrollbar(Files_I_ShowFrame,orient=tk.HORIZONTAL)
ScrollbarX.pack(side=tk.BOTTOM, fill=tk.X) ScrollbarX.pack(side=tk.BOTTOM, fill=tk.X)
ScrollbarY=ttk.Scrollbar(Files_I_ShowFrame) ScrollbarY=ttk.Scrollbar(Files_I_ShowFrame)
ScrollbarY.pack(side=tk.RIGHT, fill=tk.Y) ScrollbarY.pack(side=tk.RIGHT, fill=tk.Y)
### #
Files_I_ShowText=tk.Text( Files_I_ShowText=tk.Text(
Files_I_ShowFrame, Files_I_ShowFrame,
xscrollcommand=ScrollbarX.set, xscrollcommand=ScrollbarX.set,
yscrollcommand=ScrollbarY.set, yscrollcommand=ScrollbarY.set,
height=4, wrap='none', font=('Segoe UI',9)) height=4, wrap='none', font=('Segoe UI',9))
Files_I_ShowText.pack(side=tk.LEFT, fill=tk.BOTH, anchor='w', padx='1px', pady='1px') Files_I_ShowText.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES, anchor='w', padx='1px', pady='1px')
ScrollbarX.config(command=Files_I_ShowText.xview) ScrollbarX.config(command=Files_I_ShowText.xview)
ScrollbarY.config(command=Files_I_ShowText.yview) ScrollbarY.config(command=Files_I_ShowText.yview)
@@ -500,7 +483,7 @@ class MAINGUI:
text='Output directory') text='Output directory')
File_O_Frame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='1px') File_O_Frame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='1px')
### #
File_O_yn=ttk.Checkbutton( File_O_yn=ttk.Checkbutton(
File_O_Frame, File_O_Frame,
text='Directory', text='Directory',
@@ -513,7 +496,7 @@ class MAINGUI:
File_O_Button)) File_O_Button))
File_O_yn.pack(side=tk.LEFT, anchor='w', padx='1px', pady='0px') File_O_yn.pack(side=tk.LEFT, anchor='w', padx='1px', pady='0px')
### #
File_O_Box=ttk.Combobox( File_O_Box=ttk.Combobox(
File_O_Frame, File_O_Frame,
width=36, width=36,
@@ -521,9 +504,9 @@ class MAINGUI:
values=('',), values=('',),
state=tk.DISABLED, state=tk.DISABLED,
textvariable=self.File_O_BoxVar) textvariable=self.File_O_BoxVar)
File_O_Box.pack(side=tk.LEFT, anchor='w', padx='1px', pady='0px') File_O_Box.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES, anchor='w', padx='1px', pady='0px')
### #
File_O_Button=ttk.Button( File_O_Button=ttk.Button(
File_O_Frame, File_O_Frame,
text='...', text='...',
@@ -548,7 +531,7 @@ class MAINGUI:
command=lambda :instanceOPT(self.hwnd,self.func) command=lambda :instanceOPT(self.hwnd,self.func)
) )
Conv_Option.pack(side=tk.RIGHT, padx='1px', pady='0px') Conv_Option.pack(side=tk.RIGHT, padx='1px', pady='0px')
### #
Conv_Button=ttk.Button( Conv_Button=ttk.Button(
CtrlButtonFrame, CtrlButtonFrame,
text='Convert', text='Convert',
@@ -561,7 +544,7 @@ class MAINGUI:
self.Convertcards, self.Convertcards,
self.Exec_Progress)) self.Exec_Progress))
Conv_Button.pack(side=tk.RIGHT, padx='1px', pady='0px') Conv_Button.pack(side=tk.RIGHT, padx='1px', pady='0px')
### #
Exit_button=ttk.Button( Exit_button=ttk.Button(
CtrlButtonFrame, CtrlButtonFrame,
text='Exit', text='Exit',
@@ -569,7 +552,7 @@ class MAINGUI:
command=self.hwnd.quit) command=self.hwnd.quit)
Exit_button.pack(side=tk.LEFT, padx='1px', pady='0px') Exit_button.pack(side=tk.LEFT, padx='1px', pady='0px')
### #
StateFrame=ttk.Frame(self.ExecFrame) StateFrame=ttk.Frame(self.ExecFrame)
StateFrame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='4px') StateFrame.pack(side=tk.TOP, fill=tk.X, padx='1px', pady='4px')
self.Pro_style=ttk.Style(StateFrame) self.Pro_style=ttk.Style(StateFrame)
@@ -595,7 +578,8 @@ class MAINGUI:
maximum=100, maximum=100,
mode='determinate', mode='determinate',
style='text.Horizontal.TProgressbar') style='text.Horizontal.TProgressbar')
self.Exec_Progress.pack(side=tk.LEFT) self.Exec_Progress.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES)
class OPTIONS: class OPTIONS:
def __init__(self, parenthwnd, func): def __init__(self, parenthwnd, func):
@@ -625,7 +609,7 @@ class OPTIONS:
self.opthwnd.resizable(0, 0) self.opthwnd.resizable(0, 0)
self.func.Move_center(self.opthwnd, 310, 110) self.func.Move_center(self.opthwnd, 310, 110)
### Set icon # Set icon
icon_ico=(b'AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' icon_ico=(b'AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOrq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6' b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOrq6v/q6ur/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6'
@@ -646,11 +630,11 @@ class OPTIONS:
icon_ico=ImageTk.PhotoImage(data=icon_ico) icon_ico=ImageTk.PhotoImage(data=icon_ico)
self.opthwnd.tk.call('wm', 'iconphoto', self.opthwnd._w, icon_ico) self.opthwnd.tk.call('wm', 'iconphoto', self.opthwnd._w, icon_ico)
### Mian frame # Mian frame
self.MainFrame=ttk.Frame(self.opthwnd) self.MainFrame=ttk.Frame(self.opthwnd)
self.MainFrame.pack() self.MainFrame.pack()
### init # init
self.initOPTIONS() self.initOPTIONS()
self.initEXECTRL() self.initEXECTRL()
@@ -702,7 +686,7 @@ class OPTIONS:
state=tk.DISABLED, state=tk.DISABLED,
values=I_RTCM_BoxList) values=I_RTCM_BoxList)
I_RTCM_Box.set(I_RTCM_BoxList[0]) I_RTCM_Box.set(I_RTCM_BoxList[0])
#I_RTCM_Box['state']='readonly' # I_RTCM_Box['state']='readonly'
I_RTCM_Box.pack(side=tk.RIGHT) I_RTCM_Box.pack(side=tk.RIGHT)
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------
# Target format frame # Target format frame
@@ -743,7 +727,7 @@ class OPTIONS:
state=tk.DISABLED, state=tk.DISABLED,
values=O_RTCM_BoxList) values=O_RTCM_BoxList)
O_RTCM_Box.set(I_RTCM_BoxList[0]) O_RTCM_Box.set(I_RTCM_BoxList[0])
#O_RTCM_Box['state']='readonly' # O_RTCM_Box['state']='readonly'
O_RTCM_Box.pack(side=tk.RIGHT) O_RTCM_Box.pack(side=tk.RIGHT)
def initEXECTRL(self): def initEXECTRL(self):
@@ -782,6 +766,8 @@ class OPTIONS:
I_Format_Rnx_List=I_RINEX_BoxList.index(self.I_RINEX_Box.get()) I_Format_Rnx_List=I_RINEX_BoxList.index(self.I_RINEX_Box.get())
O_Format_Rnx_List=O_RINEX_BoxList.index(self.O_RINEX_Box.get()) O_Format_Rnx_List=O_RINEX_BoxList.index(self.O_RINEX_Box.get())
# Main program entry ---------------------------- #
if __name__ == "__main__": if __name__ == "__main__":
root=tk.Tk() root=tk.Tk()
call=CALLBACK() call=CALLBACK()
+801
View File
@@ -0,0 +1,801 @@
# @ File : rnx_convert.py
#
# @ Brief : RINEX Convert functions.
#
# Copyright (c) 2024 - 2024 KenanZhu. All Right Reserved.
#
# @ Author : KenanZhu
# @ Time stamp : [2024/11/13] First release.
# : [2024/11/14] Correct: convert of observation type of
# 'P1/P2' of GPS is correct into 'C1W/C2W'.
# : [2024/11/15] Fix bug: remove the empty observation of
# RINEX 2.xx format.
# : [2024/11/17] Add new: complish the funtions of convert
# form RINEX 3.xx to RINEX 2.xx and also support
# convert in major version of RINEX.
# : [2024/11/18] Fix bug: correct the bug when Convert RINEX file
# with comment in the middle of data will cause
# crash.
# Correct: correct the mistake in the functions
# below:
# 1. Correct function: getsys_rnx3()
# 2. Correct function: obstype3_conv()
# 3. Correct function: rnx2uni3_data()
# 4. Correct function: rnx2_data2uni()
# 5. Correct function: rnx3rnx2_data()
# Optimze: enhance the speed of converting.
#
#
# @ IDE/Editor : PyCharm / VisualStudio Code
#
# ----------------------------------------------------------------------------------------
# ---STDPORT--- #
import re
import time
import math
import threading
# ------------- #
emp=''
bnk=' '
# Get local time of converting ----------------------- #
def conv_time():
# generate convert timestamp
covpro='KNZ_Convert ver1.2.6'
covcom='COMMENT'
# Get the locate time and transform to Uni-format
y=time.localtime().tm_year
m=time.localtime().tm_mon
d=time.localtime().tm_mday
h=time.localtime().tm_hour
min=time.localtime().tm_min
sec=time.localtime().tm_sec
localtime=('%04d'%y +
'%02d'%m +
'%02d'%d +bnk+
'%02d'%h +
'%02d'%min+
'%02d'%sec+bnk+
'LLC' +bnk)
outline=(covpro +
20*bnk +
localtime+
covcom +
'\n')
return outline
# Convert data format --------------------------- #
def datefmt2_conv(inline):
hou=inline[49:51]
min=inline[52:54]
sec=inline[55:57]
return (inline[0:49] +
hou +
min +
sec +
bnk + "UTC" +
bnk + "PGM / RUN BY / DATE\n")
# Convert data format --------------------------- #
def datefmt3_conv(inline):
hou=inline[49:51]
min=inline[51:53]
sec=inline[53:55]
return (inline[0:49] +
hou + ':' +
min + ':' +
sec +
'UTCPGM / RUN BY / DATE\n')
# Convert observ type of RINEX 2.xx to Uni-format ------------- #
def obsfmt2_conv(obstypes, sys):
counter =0
obstypex=[]
obsbomit=emp
match sys:
case 'G':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P': obs='C1W'
else: obs+='C'
elif obs[1:2]=='2':
if obs[0:1]=='P': obs='C2W'
else: obs+='C'
elif obs[1:2]=='5': obs+='I'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
case 'E':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P':
obsbomit+=('%02d'%counter)
continue
else: obs+='C'
elif obs[1:2]=='5': obs+='I'
elif obs[1:2]=='6': obs+='C'
elif obs[1:2]=='7': obs+='I'
elif obs[1:2]=='8': obs+='I'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
case 'R':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P': obs='C1P'
else: obs+='C'
elif obs[1:2]=='2':
if obs[0:1]=='P': obs='C2P'
else: obs+='C'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
case 'S':
for obs in obstypes:
counter+=1
if obs[1:2]=='1':
if obs[0:1]=='P':
obsbomit+=('%02d'%counter)
continue
else: obs+='C'
elif obs[1:2]=='5': obs+='I'
else:
obsbomit+=('%02d'%counter)
continue
obstypex.append(obs)
obstypex.append(obsbomit)
return obstypex
# Convert observ msg of RINEX 2.xx to Uni-format ---------------- #
def obstype2_conv(inline, in_f, omilist):
# uni-format message label
obsobs='SYS / # / OBS TYPES'
outlines=[]
obstypes=[]
obstypex=[]
system=['G', 'E', 'R', 'S']
obsnum=int(inline[0:6])
omilist.append(obsnum)
for i in range(math.ceil(obsnum/9)):
obstypes.extend(inline[6:60].split())
if i+1==math.ceil(obsnum/9): break
inline = in_f.readline()
for sys in system:
obstypex=obsfmt2_conv(obstypes, sys)
omilist.append(obstypex[-1])
# S___2D_
num =len(obstypex)-1
outline=sys+3*bnk+'%2d'%num+bnk
for i in range(num):
outline+=obstypex[i]+bnk
if (i+1)==num:
bnx=((13-num%13)*4+1)*bnk
if num%13==0: bnx=bnk
outlines.append(outline+bnx+obsobs+'\n')
outline=0*bnk
elif (i+1)%13==0:
outlines.append(outline+bnk+obsobs+'\n')
outline=7*bnk
return outlines
# Generate the convert stamp ------------------------ #
def conv_stamp(inline, outlines, desver):
oriver=inline[ 5: 9]
# generate convert stamp
obscov='COV-OBSERVATION DATA'
obscom='COMMENT'
outlines.append( 5*bnk+
oriver+
'->' +
desver+
1*bnk+
obscov+
24*bnk+
obscom+
'\n')
outlines.append(conv_time())
outlines.append(
inline.replace('%s'%re.search(r'[1-9]\d*.\d*|0\.\d*[1-9]\d*', inline).group(), desver))
return
# Convert RINEX 2.xx(header) to Uni-format -------------- #
def rnx2_head2uni(in_f, desver, outlines, omilist):
# rinex message label
obscom='COMMENT'
obstpe='RINEX VERSION / TYPE'
obsl12='WAVELENGTH FACT L1/2'
obspro='PGM / RUN BY / DATE'
obsobs='# / TYPES OF OBSERV'
obsend='END OF HEADER'
# get input head file & convert
while True:
inline=in_f.readline()
# file is aleady checked so no need to judge again
if not inline: break
if inline.find(obscom)>=0: outlines.append(inline)
elif inline.find(obspro)>=0: outlines.append(datefmt2_conv(inline))
elif inline.find(obsobs)>=0: outlines.extend(obstype2_conv(inline, in_f, omilist))
elif inline.find(obstpe)>=0: conv_stamp(inline, outlines, desver)
elif inline.find(obsl12)>=0: continue
elif inline.find(obsend)>=0: break
else : outlines.append(inline)
# get the end message after break
outlines.append(60*bnk+obsend+'\n')
return 1
# Convert RINEX 2.xx(data) to Uni-Format ------------------- #
def rnx2_data2uni(in_f, outlines, omilist, obsnum):
sPRN=[]
scounter=0
outline=emp
# return false when file end
inline=in_f.readline()
if not inline: return False
if not inline[28:29]=='0':
outlines.append(inline)
for i in range(int(inline[29:32])):
outlines.append(in_f.readline())
return True
# get epoch message & flag state
year = int(inline[0 :3])
month = int(inline[0+3 :3+3])
day = int(inline[0+3+3 :3+3+3])
hour = int(inline[0+3+3+3 :3+3+3+3])
minute= int(inline[0+3+3+3+3 :3+3+3+3+3])
second=float(inline[0+3+3+3+3+3 :3+3+3+3+3+11])
e_flag= int(inline[0+3+3+3+3+3+11 :3+3+3+3+3+11+3])
satnum= int(inline[0+3+3+3+3+3+11+3:3+3+3+3+3+11+3+3])
if not e_flag==0: return True
# time to Uni-Format
outlines.append(
"> %4d %02d %02d %02d %02d %10.7f %2d %2d\n" \
% (year + 2000, month, day, hour, minute, second, e_flag, satnum))
# get list of sPRN
for i in range(math.ceil(satnum/12)):
if not i==0 : inline =in_f.readline()
if i+1< math.ceil(satnum/12): scounter=12
if i+1==math.ceil(satnum/12): scounter=satnum-i*12
for j in range(scounter):
sPRN.append(inline[32+3*j:32+3*(j+1)].replace(bnk,'0'))
# convert epoch data to Uni-Format
for i in range(satnum):
oulist=[]
outline=emp
# get omit list
match sPRN[i][0:1]:
case 'G': omitlis=omilist[0]
case 'E': omitlis=omilist[1]
case 'R': omitlis=omilist[2]
case 'S': omitlis=omilist[3]
# observ amount lower than 5. no need to enter
if obsnum <= 5:
outline=in_f.readline()
outline=inline.replace('\n',emp).replace('\r',emp)
# elif, need to enter
elif obsnum > 5:
for j in range(math.ceil(obsnum/5)):
inline=in_f.readline()
outline+=inline.replace('\n',emp).replace('\r',emp)
if len(outline)%16==15: outline+=bnk
if len(outline)%16==14: outline+=2*bnk
outline+=(5 - int(len(outline)/16))*16*bnk
# omit the empty observ by ''
# outline be divide into parts
for j in range(obsnum):
oulist.append(outline[16*j:16*j+16])
for j in range(len(omitlis)):
oulist[omitlis[j]-1]=emp
# remerge into outline
outline=emp.join(oulist)
outlines.append(sPRN[i]+outline+"\n")
return True
# Convert Uni-format to RINEX 3.xx --------------------- #
def uni2rnx3_data(outlines, out_f):
out_f.writelines(outlines)
return
# Get observ type need be omitted --------------------- #
def getomit(omilist):
for i in range(len(omilist)):
sysomit=[]
omitlis=omilist[0]
omitnum=len(omitlis)/2
for j in range(int(omitnum)):
sysomit.append(int(omitlis[j*2+0:j*2+2]))
omilist.append(sysomit)
omilist.pop(0)
return
# Convert RINEX 2.xx to Des-format --------------------- #
def rnx2des(in_path, out_path, oriver, desver):
headread=False
outlines=[]
omilist=[]
in_f=open(in_path, 'r')
out_f=open(out_path, 'a')
# convert RINEX 2.xx(header) to Uni-format
match rnx2_head2uni(in_f, desver, outlines, omilist):
case 1: headread=True
case 0: headread=False
if headread:
# convert Uni-foramt(header) to Des-format
match desver[0:1]:
case '2': addemp=uni2rnx2_data(4, outlines, out_f, True, None)
case '3': uni2rnx3_data(outlines, out_f)
outlines=[]
# get each observ type and omit the empty
obsnum=omilist[0]
omilist.pop(0)
getomit(omilist)
# convert RINEX 2.xx(data) to Uni-format
while rnx2_data2uni(in_f, outlines, omilist, obsnum):
# convert Uni-foramt(data) to Des-format
match desver[0:1]:
case '2': uni2rnx2_data(4, outlines, out_f, False, addemp)
case '3': uni2rnx3_data(outlines, out_f)
outlines=[]
elif not headread: return 0
in_f.close()
out_f.close()
return 1
# Get the position of new add observ type ----------------- #
def getaddemp(alltypes, sysstype):
addemp=[[],[],[],[]]
for i in range(len(sysstype)):
for j in range(len(sysstype[i])):
for k in range(len(alltypes)):
if sysstype[i][j]==alltypes[k]:
addemp[i].append(k)
return addemp
# Filter the same obs fmt in RINEX 2.xx ----------------- #
def obsfmt_filter(alltypes):
empemp=[]
spos=npos=0
empemp.append(len(alltypes))
while True:
npos+=1
if spos==empemp[0]: break
if npos==empemp[0]:
spos+=1
npos=spos
continue
if not alltypes[spos]: continue
if not alltypes[npos]: continue
if alltypes[spos]==alltypes[npos]:
alltypes[npos]=emp
empemp.append(npos)
# restruct the data
totnum=empemp[0]
empemp=sorted(empemp[1:])
empemp.insert(0,totnum)
empemp.clear()
# addemp.append(empemp)
# clear the empty
alltypes=[obs for obs in alltypes if obs!=emp]
return alltypes
# Sort by observ type --------------------------- #
def sort_observ(alltypes):
alltypex=[]
temtypes=[]
freindex=['1','2','3','4',
'5','6','7','8']
obsetype=['C','L','D','S']
for type in alltypes:
freidx=freindex.index(type[1:2])/ 1
if type=='P1C' or type=='P2C':
tyeidx=obsetype.index(type[2:3])/10
else:
tyeidx=obsetype.index(type[0:1])/10
alltypex.append(freidx+tyeidx)
sdx=ndx=0
while True:
if len(alltypex)==0:
break
if (ndx+1)==len(alltypex):
temtypes.append(alltypes[sdx])
alltypes.pop(sdx)
alltypex.pop(sdx)
sdx=ndx=0
continue
ndx+=1
if alltypex[ndx]<=alltypex[sdx]:
sdx=ndx
continue
return temtypes
# Convert observ type of RINEX 3.xx to Uni-Format --------------- #
def obsfmt3_conv(alltypes):
alltypex=[]
for type in alltypes:
if type[1:2]=='1':
if type=='C1W': type='P1'
elif type[1:2]=='2':
if type=='C2W': type='P2'
else: pass
alltypex.append(type[0:2])
return alltypex
# Convert observ msg of RINEX 3.xx to Uni-Format ---------------- #
def obstype3_conv(i, sysnum, outlines, outlinex):
alltypes=[]
obstypes=[]
sysstype=[[],[],[],[]]
# rinex message label
obsobs='# / TYPES OF OBSERV'
for j in range(sysnum):
obstypes=[]
# reject system not support in Uni-Format
match outlines[i][0:1]:
case 'G': obstypes.append(0)
case 'E': obstypes.append(1)
case 'R': obstypes.append(2)
case 'S': obstypes.append(3)
case 'C':
i+=math.ceil(int(outlines[i][1:6])/13)
continue
case 'I':
i+=math.ceil(int(outlines[i][1:6])/13)
continue
case 'J':
i+=math.ceil(int(outlines[i][1:6])/13)
continue
case _:
i+=1
continue
obsnum=int(outlines[i][1:6])
for k in range(math.ceil(obsnum/13)):
obstypes.extend(outlines[i][6:60].split())
if k+1==math.ceil(obsnum/13): break
i+=1
# check the differ observ type in glonass
if obstypes[0]==2:
try:
pos=obstypes.index('C1P')
obstypes[pos]='P1C'
except ValueError: pass
try:
pos=obstypes.index('C2P')
obstypes[pos]='P2C'
except ValueError: pass
sysstype[obstypes[0]]=obsfmt3_conv(obstypes[1:])
alltypes=list(set(obstypes[1:]+alltypes))
i+=1
# sort observ types
alltypes=sort_observ(alltypes)
# convert observ type into des-format
alltypes=obsfmt3_conv(alltypes)
# fliter the same observ type
alltypes=obsfmt_filter(alltypes)
# get new added pos
addemp=getaddemp(alltypes, sysstype)
num =len(alltypes)
outline=4*bnk+'%2d'%num
for j in range(num):
outline+=4*bnk+alltypes[j]
if (j+1)==num:
bnx=((9-num%9)*6)*bnk
if num%9==0: bnx=emp
outlinex.append(outline+bnx+obsobs+'\n')
outline=0*bnk
elif (j+1)%9==0:
bnx=0*bnk
outlinex.append(outline+bnx+obsobs+'\n')
outline=6*bnk
# get amout of observ tyeps
addemp.append(num)
return addemp
# Get the total amount of sat system -------------------- #
def getsys_rnx3(in_f):
# rinex message label
obsobs='SYS / # / OBS TYPES'
readst=False
syslis=[]
sysnum=0
while True:
inline=in_f.readline()
if not inline: break
if inline.find(obsobs)>=0:
readst=True
sys=inline[0:1]
if sys==bnk: continue
if sys not in syslis:
syslis.append(sys)
sysnum+=1
elif readst and inline.find(obsobs)<0: break
in_f.seek(0)
return sysnum
# Convert head_data of RINEX 3.xx to Uni-format -------------- #
def rnx3_head2uni(in_f, desver, outlines):
# rinex message label
# obscom='COMMENT'
obstpe='RINEX VERSION / TYPE'
# obspro='PGM / RUN BY / DATE'
# obsobs='SYS / # / OBS TYPES'
obsshi='SYS / PHASE SHIFT'
obsrsb='GLONASS COD/PHS/BIS'
obsrsl='GLONASS SLOT / FRQ #'
obsend='END OF HEADER'
# get input head file & convert
while True:
inline=in_f.readline()
# file is aleady checked so no need to judge again
if not inline: break
#if inline.find(obscom)>=0: outlines.append(inline)
#elif inline.find(obspro)>=0: outlines.append(inline)
#elif inline.find(obsobs)>=0: outlines.append(inline)
elif inline.find(obstpe)>=0: conv_stamp(inline, outlines, desver)
elif inline.find(obsshi)>=0: continue
elif inline.find(obsrsb)>=0: continue
elif inline.find(obsrsl)>=0: continue
elif inline.find(obsend)>=0: break
else : outlines.append(inline)
# get the end message after break
outlines.append(60*bnk+obsend+'\n')
return 1
# Convert obs of RINEX 3.xx to Uni-format ------------------- #
def rnx2uni3_data(outlines, in_f):
inline=in_f.readline()
if not inline: return False
if not inline[0:1]=='>': return True
if not int(inline[30:32])==0: return True
outlines.append(inline)
satnum=int(inline[33:35])
for i in range(satnum):
outlines.append(in_f.readline())
return True
# Convert RINEX 3.xx(epoch msg) to RINEX 2.xx --------------- #
def rnx3rnx2_epo(outline, sPRN):
outlines=[]
datedat=outline.split()
# omit data when flag !=0
if not int(datedat[7])==0: return outlines
outline=(
'%3d%3d%3d%3d%3d%11.7f%3d%3d'
%(int(datedat[1])-2000,int(datedat[2]), int(datedat[3]),# |year |month |day
int(datedat[4]) ,int(datedat[5]),float(datedat[6]),# |hour |minute |second
int(datedat[7]) ,len(sPRN))) # |flag |satnum |
# convert to RINEX 2.xx
satnum=len(sPRN)
for i in range(satnum):
outline+=sPRN[i]
if (i+1)==len(sPRN):
outlines.append(outline+'\n')
elif (i+1)%12==0:
outlines.append(outline+'\n')
outline=32*bnk
return outlines
# Convert RINEX 3.xx(data of each sat) to RINEX 2.xx ------------ #
def rnx3rnx2_dat(addemp, obsnum, outline):
outlines=[]
num=range(len(addemp))
empdat=16*bnk
outlinx=[empdat for i in range(obsnum)]
for i,j in zip(num,addemp):
if not outlinx[j]==empdat: continue
if outline[i*16+3:i*16+16+3]:
outlinx[j]=outline[i*16+3:i*16+16+3]\
.replace('\n',2*bnk).replace('\r',2*bnk)
else: continue
outline=emp
for i in range(len(outlinx)):
outline+=outlinx[i]
for i in range(math.ceil(len(outline)/80)):
outlines.append(outline[i*80:i*80+80]+'\n')
return outlines
# Convert RINEX 3.xx to RINEX 2.xx --------------------- #
def rnx3rnx2_data(outlines, addemp):
sPRN=[]
datline=[]
outlinex=[]
# filter the sat system ecxcept:#
# ---GPS-Galileo-GLONASS-SBAS---#
for i in range(len(outlines)):
if outlines[i][0:1]=='G': pass
elif outlines[i][0:1]=='E': pass
elif outlines[i][0:1]=='R': pass
elif outlines[i][0:1]=='S': pass
else: continue
datline.append(i)
sPRN.append(outlines[i][0:3])
outlinex.extend(rnx3rnx2_epo(outlines[0], sPRN))
for i in datline:
if outlines[i][0:1]=='G': idx=0
elif outlines[i][0:1]=='E': idx=1
elif outlines[i][0:1]=='R': idx=2
elif outlines[i][0:1]=='S': idx=3
else: continue
outlinex.extend(rnx3rnx2_dat(addemp[idx], addemp[4], outlines[i]))
return outlinex
# Convert Uni-format to RINEX 2.xx --------------------- #
def uni2rnx2_data(sysnum, outlines, out_f, headordata, add_emp):
# rinex message label
obspro='PGM / RUN BY / DATE'
obsobs='SYS / # / OBS TYPES'
obsend='END OF HEADER'
outlinex=[]
obsread=False
# convert Uni-format to RINEX 2.xx(header)
if headordata:
for i in range(len(outlines)):
if outlines[i].find(obspro)>=0: outlinex.append(datefmt3_conv(outlines[i]))
elif outlines[i].find(obsobs)>=0:
if not obsread:
obsread=True
addemp=obstype3_conv(i, sysnum, outlines, outlinex)
else: continue
elif outlines[i].find(obsend)>=0: outlinex.append(outlines[i])
else:outlinex.append(outlines[i])
# convert Uni-format to RINEX 2.xx(data)
elif not headordata:
if not outlines: return 0
outlinex=rnx3rnx2_data(outlines, add_emp)
# output to destination file
out_f.writelines(outlinex)
if headordata: return addemp
elif not headordata: return 0
# Convert data of RINEX 3.xx to Des-format ----------------- #
def rnx3des(in_path, out_path, oriver, desver):
headread=False
outlines=[]
in_f=open(in_path, 'r')
out_f=open(out_path, 'a')
# get the amount of sat system
sysnum=getsys_rnx3(in_f)
# convert RINEX 3.xx(header) to Uni-format
match rnx3_head2uni(in_f, desver, outlines):
case 1: headread=True
case 0: headread=False
# convert Uni-foramt(header) to Des-format
if headread:
match desver[0:1]:
case '2': addemp=uni2rnx2_data(sysnum, outlines, out_f, True, None)
case '3': uni2rnx3_data(outlines, out_f)
outlines=[]
# convert RINEX 3.xx(data) to Uni-format
while rnx2uni3_data(outlines, in_f):
# convert Uni-foramt(data) to Des-format
match desver[0:1]:
case '2': uni2rnx2_data(sysnum, outlines, out_f, False, addemp)
case '3': uni2rnx3_data(outlines, out_f)
outlines=[]
elif not headread: return 0
in_f.close()
out_f.close()
return 1
# Reject data of invaild input ----------------------- #
def rnx_reject(in_path, out_path, oriver):
# rinex message label
obstpe='RINEX VERSION / TYPE'
obsmsg='OBSERVATION DATA'
obsend='END OF HEADER'
# reject cause cant input file
try:
with open(in_path, 'r'): pass
except FileNotFoundError: return 0
# reject wrong format or version
in_f=open(in_path, 'r')
while True:
inline=in_f.readline()
if not inline: return 0
if inline.find(obstpe)>=0:
if not inline.find(obsmsg)>=0: return 0
if not inline[ 5: 6]==oriver : return 0
elif inline.find(obsend)>=0: break
# reject cause cant output file
try:
with open(out_path, 'w'): pass
except PermissionError: return 0
return 1
# ---------------------------------------------------------------------------------------
# CONVERT FUNCTION
#
# Brief :Determine the file format and assign handlers
# Return :
# queue msg==0: fail.
# queue msg==1: ok.
# ---------------------------------------------------------------------------------------
def Convert_Un(in_path, out_path, oriver, desver, resqueue):
file_state=False
match rnx_reject(in_path, out_path, oriver):
case 1: file_state=True
case 0: file_state=False
if file_state:
match oriver:
case '2': rnx2des(in_path, out_path, oriver, desver)
case '3': rnx3des(in_path, out_path, oriver, desver)
else : return resqueue.put(0)
return resqueue.put(1)
# Multi-threading task --------------------------- #
def _Convert_Un(in_path, out_path, oriver, desver, resqueue):
T=threading.Thread(
target=lambda :Convert_Un(in_path, out_path, oriver, desver, resqueue)
)
T.start()
T.join()
return resqueue.get()
+18
View File
@@ -0,0 +1,18 @@
# @ File : rtcm_convert.py.py
#
# Copyright (c) 2024 - 2024 KenanZhu. All Right Reserved.
#
# @ Author : KenanZhu
# @ Time : 2024/11/16
# @ Brief :
# :
# @ IDE : PyCharm
#
# ----------------------------------------------------------------------------------------
# ---SELPORT--- #
# ---STDPORT--- #
# ------------- #
def Convert_Un():
return 0