Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement nqm2ms2 #16

Merged
merged 109 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
9a85dd0
initial design of schema structure and implementation of ANTENNA schema
tnakazato Dec 29, 2024
8ec73fe
bug fix on PositionColumn: remove maxlen
tnakazato Dec 29, 2024
66bf432
add prototype table builder
tnakazato Dec 29, 2024
a8eea37
backend option to build MS: python-casacore or casa6 (casatools)
tnakazato Dec 29, 2024
1a0e43f
implement MAIN table schema
tnakazato Dec 30, 2024
53415b4
add prefix Ms to dataclasses
tnakazato Dec 30, 2024
2103d73
bug fix on array column
tnakazato Dec 31, 2024
5072709
refactoring dataclasses
tnakazato Dec 31, 2024
02c696c
remove unused module
tnakazato Dec 31, 2024
eada58a
add subtable schema
tnakazato Dec 31, 2024
c985c92
configure logger
tnakazato Jan 1, 2025
9c4322f
complete implementing build_ms2
tnakazato Jan 1, 2025
3f02263
define ms2._table submodule
tnakazato Jan 2, 2025
698f2f3
implement fill_observation
tnakazato Jan 2, 2025
26f2101
implement fill_procesor
tnakazato Jan 2, 2025
43dfd02
rename _table submodule to _casa
tnakazato Jan 2, 2025
8da99d4
implement fill_field
tnakazato Jan 2, 2025
74fe728
more implementation of filler
tnakazato Jan 3, 2025
1f8579b
fill all subtables
tnakazato Jan 9, 2025
f5d0c6c
fill main
tnakazato Jan 11, 2025
f9e157d
bug fix on read_only option of _casa6.open_table
tnakazato Jan 11, 2025
e7d5040
instruction on nqm2ms2
tnakazato Jan 11, 2025
c2c40a6
bugfix on processor_id and scan_number
tnakazato Jan 11, 2025
70b49a1
add MS2 schema test (MAIN)
tnakazato Jan 12, 2025
632bcb2
address flake8 errors
tnakazato Jan 12, 2025
d3d6238
address black error
tnakazato Jan 12, 2025
5b10eef
bug fix
tnakazato Jan 12, 2025
aa2977d
fix black errors in test code
tnakazato Jan 12, 2025
047d51e
more black fix on test_main.py
tnakazato Jan 12, 2025
9f97f7a
disable pylint checkers
tnakazato Jan 12, 2025
ed84648
initialize casa data directory
tnakazato Jan 12, 2025
fcd6db1
fix
tnakazato Jan 12, 2025
39cf822
Revert "fix"
tnakazato Jan 12, 2025
3d71a53
update CI.yaml
tnakazato Jan 12, 2025
dea6dc6
set runs-on
tnakazato Jan 12, 2025
8667297
rewrite CI.yml with microsoft/action-python
tnakazato Jan 12, 2025
5cd2460
checkout repo
tnakazato Jan 12, 2025
7c2f38f
bug fix
tnakazato Jan 12, 2025
3213a72
python version set to 3.12
tnakazato Jan 12, 2025
f66e3e6
explicitly specify workdir
tnakazato Jan 12, 2025
76659a6
fix wrong inputs
tnakazato Jan 12, 2025
71b0370
fix wrong inputs
tnakazato Jan 12, 2025
7c269dd
set workdir to '.'
tnakazato Jan 13, 2025
0856b87
manually install nro45data
tnakazato Jan 13, 2025
3dd764b
possible fix for pip install error
tnakazato Jan 13, 2025
514f4aa
fix wrong optional dependency name
tnakazato Jan 13, 2025
a379cca
install casatools explicitly
tnakazato Jan 13, 2025
47c9c11
switch python version to 3.10
tnakazato Jan 13, 2025
7fa9fe8
use ubuntu-24.04
tnakazato Jan 13, 2025
f30622e
use ubuntu-22.04
tnakazato Jan 13, 2025
20644a9
manually configure pytest
tnakazato Jan 13, 2025
9d71ee5
changed pytest marker strategy
tnakazato Jan 13, 2025
9efa153
verbose output for pytest
tnakazato Jan 13, 2025
f4c77e1
rename schema test
tnakazato Jan 13, 2025
b84707b
remove unused imports
tnakazato Jan 13, 2025
c77b3c4
add schema test for ANTENNA subtable
tnakazato Jan 13, 2025
d465d21
add schema test for DATA_DESCRIPTION subtable
tnakazato Jan 13, 2025
6259c5e
add schema test for FEED subtable
tnakazato Jan 13, 2025
94942a5
schema bug fix on FEED subtable
tnakazato Jan 13, 2025
0b4b8ea
fix schema for FIELD subtable
tnakazato Jan 13, 2025
56c85ea
add schema test for FIELD subtable
tnakazato Jan 13, 2025
d4f27e1
add schema test for FLAG_CMD subtable
tnakazato Jan 13, 2025
6ec6de3
fix schema for HISTORY subtable
tnakazato Jan 13, 2025
2fa3eb2
add schema test for HISTORY subtable
tnakazato Jan 13, 2025
37788a0
fix schema for OBSERVATION subtable
tnakazato Jan 13, 2025
d461622
add schema test for OBSERVATION subtable
tnakazato Jan 13, 2025
438fd88
black fix for antenna schema test
tnakazato Jan 13, 2025
56f7f7c
add schema test for POINTING subtable
tnakazato Jan 13, 2025
436432c
add schema test for POLARIZATION subtable
tnakazato Jan 13, 2025
66c20dd
add schema test for PROCESSOR subtable
tnakazato Jan 13, 2025
c30f26e
fix schema for SOURCE subtable
tnakazato Jan 13, 2025
c701bf9
add schema test for SOURCE subtable
tnakazato Jan 13, 2025
1f15768
update checker
tnakazato Jan 13, 2025
41682a7
add schema test for SPECTRAL_WINDOW subtable
tnakazato Jan 13, 2025
c6f1791
add schema test for STATE subtable
tnakazato Jan 13, 2025
7aa7325
fix schema for SYSCAL subtable
tnakazato Jan 13, 2025
4cc453c
add schema test for SYSCAL subtable
tnakazato Jan 13, 2025
e563202
add schema test for WEATHER subtable
tnakazato Jan 13, 2025
9e42258
refactor schema code
tnakazato Jan 13, 2025
9340e85
bug fix on MAIN schema
tnakazato Jan 13, 2025
d455a9b
implement nqm2ms2
tnakazato Jan 13, 2025
bb4a8d8
put keywords to connect MAIN table with subtables
tnakazato Jan 16, 2025
1cec951
connect HISTORY and FLAG_CMD subtables as well
tnakazato Jan 16, 2025
b968a85
fix schema bugs in ANTENNA and HISTORY
tnakazato Jan 16, 2025
6bfdb8b
reorganization of test directory
tnakazato Jan 16, 2025
bd715ab
update tests to pass with casacore-python
tnakazato Jan 16, 2025
cad52a7
bug fix on FEED.POL_TYPE
tnakazato Jan 17, 2025
5eba38d
bug fix on SYSCAL table filler
tnakazato Jan 17, 2025
c4dcec1
define utility module for testing
tnakazato Jan 18, 2025
769c813
bug fix on OBSERVATION.TIME_RANGE
tnakazato Jan 25, 2025
d5712e5
utility function to convert MJD to datetime object
tnakazato Jan 25, 2025
520b35b
change handling of POLARIZATION.CORR_TYPE when npol is 1
tnakazato Jan 25, 2025
4113212
add data for tests
tnakazato Jan 25, 2025
eaf20c0
fix relative import error
tnakazato Jan 25, 2025
2e89341
add test for ms2 filler
tnakazato Jan 25, 2025
00b58fd
add fixture to propagate data path and enabled nqm2fits test
tnakazato Jan 25, 2025
14e3a3e
remove test utility module
tnakazato Jan 25, 2025
4121c36
fixed confused use of JST and UTC
tnakazato Jan 25, 2025
c467b0f
replace datetime.UTC with datetime.timezone.utc
tnakazato Jan 25, 2025
70d9add
changed a way to compare datetime object
tnakazato Jan 25, 2025
3ef56a2
another update on testing TIME
tnakazato Jan 25, 2025
22e7f2e
explicitly set time zone for casa6
tnakazato Jan 25, 2025
0da8e3e
refactoring test_h40
tnakazato Jan 26, 2025
8cf00eb
stanardize test name
tnakazato Jan 26, 2025
d15a770
add test for z45 data
tnakazato Jan 26, 2025
d9b6db4
bug fix on assignment of DATA_DESC_ID
tnakazato Feb 1, 2025
d5c11f3
bug fix in test code: number of rows per scan
tnakazato Feb 1, 2025
d690fc4
add test for FOREST data
tnakazato Feb 1, 2025
99d63e3
Merge branch 'main' into implement-nqm2ms2
tnakazato Feb 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 65 additions & 4 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,71 @@ on:
workflow_dispatch:

jobs:
validation:
uses: microsoft/action-python/.github/workflows/validation.yml@0.7.3
with:
workdir: '.'
linting:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Black
uses: microsoft/action-python@0.7.0
with:
black: true
python_version: 3.12
workdir: '.'

- name: Bandit
uses: microsoft/action-python@0.7.0
with:
bandit: true
python_version: 3.12
workdir: '.'

- name: Pylint
uses: microsoft/action-python@0.7.0
with:
pylint: true
python_version: 3.12
workdir: '.'

# - name: Pyright
# uses: microsoft/action-python@0.7.0
# with:
# pyright: true

- name: Flake8
uses: microsoft/action-python@0.7.0
with:
flake8: true
python_version: 3.12
workdir: '.'

testing:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Initialize CASA data directory
run: mkdir -p ~/.casa/data

- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: '3.10'
architecture: 'x64'

- name: install nro45data
run: pip install -e .[casa6,test]

- name: Pytest
run: pytest -v

# validation:
# needs: init_casadata
# uses: microsoft/action-python/.github/workflows/validation.yml@0.7.3
# with:
# workdir: '.'

# publish:
# uses: microsoft/action-python/.github/workflows/publish.yml@0.6.4
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ IO utility module to read Position-Switch (PSW) data of Nobeyama 45m telescope.
```
git clone https://github.com/tnakazato/nro45data.git
cd nro45data
pip install .
pip install .[casa6]
```

## Usage
Expand All @@ -18,3 +18,10 @@ pip install .
import nro45data.psw as psw
psw.nqm2fits('mydata.nqm', 'mydata.fits')
```

`nqm2ms2` converts NRO 45m PSW data (.nqm) to MS2.

```python
import nro45data.psw as psw
psw.nqm2ms2('mydata.nqm', 'mydata.ms')
```
1 change: 0 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
exclude_patterns = []



# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

Expand Down
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ dependencies = [
]

[project.optional-dependencies]
casa6 = [
"casatools"
]
casacore = [
"python-casacore"
]
spark = [
"pyspark>=3.0.0"
]
Expand Down Expand Up @@ -178,6 +184,7 @@ limit-inference-results=100
persistent="yes"
suggestion-mode="yes"
unsafe-load-any-extension="no"
disable="W,C,R"

[tool.pylint.'MESSAGES CONTROL']
enable="c-extension-no-member"
Expand Down
10 changes: 10 additions & 0 deletions src/nro45data/psw/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import logging

from .io import _read_psw
from .io import _to_fits
from .ms2 import _to_ms2
from .ms4 import _to_ms4


logging.basicConfig(
format="%(asctime)s\t%(levelname)s\t%(funcName)s\t%(message)s",
# format='%(levelname)s %(message)s',
datefmt="%Y/%m/%d %H:%M:%S",
level=logging.INFO,
)


def nqm2fits(nqmfile: str, fitsfile: str, overwrite: bool = False) -> bool:
"""Convert NRO45m PSW data (.nqm) to FITS.

Expand Down
4 changes: 3 additions & 1 deletion src/nro45data/psw/io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .reader import _read_psw
from .fits import _to_fits
from .fits import _to_fits

__all__ = ["_read_psw", "_to_fits"]
12 changes: 4 additions & 8 deletions src/nro45data/psw/io/fits.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import List, Tuple, TYPE_CHECKING
from typing import TYPE_CHECKING

import astropy.io.fits as fits

if TYPE_CHECKING:
from astropy.io.fits.hdu.hdulist import HDUList


def _to_fits(hdulist: 'HDUList', fitsfile: str, overwrite: bool = False) -> bool:
def _to_fits(hdulist: "HDUList", fitsfile: str, overwrite: bool = False) -> bool:
"""Export HDUList to FITS.

Args:
Expand All @@ -24,13 +24,9 @@ def _to_fits(hdulist: 'HDUList', fitsfile: str, overwrite: bool = False) -> bool
output_hdulist = fits.HDUList([primary_hdu, hdulist[0]])

try:
output_hdulist.writeto(
fitsfile,
output_verify='fix+warn',
overwrite=overwrite
)
output_hdulist.writeto(fitsfile, output_verify="fix+warn", overwrite=overwrite)
except Exception as e:
print(e)
status = False

return status
return status
39 changes: 15 additions & 24 deletions src/nro45data/psw/io/reader.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import collections
import os
import re
import tempfile
from typing import List, Tuple, TYPE_CHECKING
from typing import List, Tuple

from astropy.io.fits.hdu.hdulist import HDUList

Expand All @@ -21,7 +20,7 @@ def _is_nro_psw(filename: str) -> bool:
True if the file is in NRO 45m PSW format, otherwise False.
"""
expected = "XTENSION='BINTABLE'"
with open(filename, 'rb') as f:
with open(filename, "rb") as f:
first_record = f.read(FITS_RECORD_SIZE).decode()

return first_record.startswith(expected)
Expand All @@ -36,7 +35,7 @@ def _read_header_and_data(filename: str) -> Tuple[List[str], bytes]:
Returns:
List of header records and binary data
"""
with open(filename, 'rb+') as f:
with open(filename, "rb+") as f:
# header
header = []
is_end_of_header = False
Expand All @@ -50,7 +49,7 @@ def _read_header_and_data(filename: str) -> Tuple[List[str], bytes]:
num_records += 1
record = record_bytes.decode()
header.append(record)
is_end_of_header = record.strip() == 'END'
is_end_of_header = record.strip() == "END"
if is_end_of_header:
break

Expand All @@ -74,20 +73,19 @@ def _follow_fits_standard(records: List[str]) -> List[str]:
Returns:
List of tweaked header records
"""

def __insert_space_before_value(record: str) -> str:
if record.startswith('END'):
if record.startswith("END"):
return record

fixed_record = re.sub('=', '= ', record, count=1)
if ' /' in fixed_record:
fixed_record = fixed_record.replace(' /', '/')
elif fixed_record.endswith(' '):
fixed_record = re.sub("=", "= ", record, count=1)
if " /" in fixed_record:
fixed_record = fixed_record.replace(" /", "/")
elif fixed_record.endswith(" "):
fixed_record = fixed_record[:-1]
return fixed_record

return list(map(
__insert_space_before_value, records
))
return list(map(__insert_space_before_value, records))


def _rename_duplicate_types(records: List[str]) -> List[str]:
Expand All @@ -101,7 +99,7 @@ def _rename_duplicate_types(records: List[str]) -> List[str]:
"""
duplicate_rows = collections.defaultdict(list)
for i, r in enumerate(records):
if r.startswith('TTYPE'):
if r.startswith("TTYPE"):
k = re.match(r".*= '([^']+)'.*", r)[1]
duplicate_rows[k].append(i)
fixed_records = records[::]
Expand All @@ -124,10 +122,7 @@ def _read_psw(filename: str) -> HDUList:
mode: Observation mode. Either 'psw' or 'otf'.
"""
if not _is_nro_psw(filename):
raise RuntimeError(
'Incompatible data: '
f'"{filename}" is not in NRO 45m PSW format.'
)
raise RuntimeError("Incompatible data: " f'"{filename}" is not in NRO 45m PSW format.')

record_list, binary_data = _read_header_and_data(filename)

Expand All @@ -138,12 +133,8 @@ def _read_psw(filename: str) -> HDUList:
# for r in record_list:
# print(r)

header = ''.join(record_list).encode()
header = "".join(record_list).encode()

hdulist = HDUList.fromstring(
header + binary_data,
ignore_missing_simple=True,
lazy_load_hdus=False
)
hdulist = HDUList.fromstring(header + binary_data, ignore_missing_simple=True, lazy_load_hdus=False)

return hdulist
26 changes: 20 additions & 6 deletions src/nro45data/psw/ms2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import logging
import os
import shutil
from typing import TYPE_CHECKING

from nro45data.psw.ms2.builder import build_ms2
from nro45data.psw.ms2.filler import fill_ms2

if TYPE_CHECKING:
from astropy.io.fits.hdu.hdulist import HDUList

LOG = logging.getLogger(__name__)


def _to_ms2(
hdulist: 'HDUList',
msfile: str,
overwrite: bool = False
) -> bool:
def _to_ms2(hdulist: "HDUList", msfile: str, overwrite: bool = False) -> bool:
"""Export HDUList to MeasurementSet v2.

Not implemented yet.
Expand All @@ -22,4 +26,14 @@ def _to_ms2(
Returns:
Export status. True is successful.
"""
return False
if os.path.exists(msfile) and not overwrite:
raise FileExistsError(f"Abort since {msfile} exists and overwrite is False")

if os.path.exists(msfile):
LOG.info(f'Overwrite existing {msfile}...')
shutil.rmtree(msfile)

build_ms2(msfile)
fill_ms2(msfile, hdulist[0])

return True
31 changes: 31 additions & 0 deletions src/nro45data/psw/ms2/_casa/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from ._casa6 import _is_casa6_available
from ._casacore import _is_casacore_available

if _is_casa6_available:
from ._casa6 import build_table
from ._casa6 import open_table
from ._casa6 import _table
from ._casa6 import convert_str_angle_to_rad
from ._casa6 import put_table_keyword
from ._casa6 import datestr2mjd
from ._casa6 import mjd2datetime
elif _is_casacore_available:
from ._casacore import build_table
from ._casacore import open_table
from ._casacore import _table
from ._casacore import convert_str_angle_to_rad
from ._casacore import put_table_keyword
from ._casacore import datestr2mjd
from ._casacore import mjd2datetime
else:
raise ModuleNotFoundError("Neither casatools or python-casacore is available")

__all__ = [
"build_table",
"open_table",
"_table",
"convert_str_angle_to_rad",
"put_table_keyword",
"datestr2mjd",
"mjd2datetime"
]
Loading
Loading