make_sigmf.py 6.84 KB
Newer Older
Thomas Telkamp's avatar
Thomas Telkamp committed
1
2
3
#!/usr/bin/env python3

import os
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
4
import time
Thomas Telkamp's avatar
Thomas Telkamp committed
5
from astropy.time import Time
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
6
from astropy.coordinates import SkyCoord
Thomas Telkamp's avatar
Thomas Telkamp committed
7
8
9
10
11
12
13
import json
from urllib.request import urlopen
import astropy.units as u
from astropy.table import Table

from argparse import ArgumentParser

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
14
from telescope import Telescope
Thomas Telkamp's avatar
Thomas Telkamp committed
15

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
16
17

def make_sigmf(iqfile, no_rename=False):
Thomas Telkamp's avatar
Thomas Telkamp committed
18
19
20
21
22
23
24
25
26
27
    # time_first_sample, rate, frequency, rx_gain, bandwidth, reference, time_source, format, channel, antenna, sdr_type

    filename_metadata = iqfile + ".metadata"
    with open(filename_metadata, "r") as file_metadata:
        _ = file_metadata.readline()
        metadata = file_metadata.readline().strip().split(", ")
    time_first_sample, rate, frequency, rx_gain, bandwidth, reference, time_source, dataformat, channel, antenna, sdr_type, num_samples = metadata
    rate = float(rate)
    frequency = float(frequency)
    rx_gain = float(rx_gain)
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
28
    time_first_sample = Time(float(time_first_sample), format="unix")
Thomas Telkamp's avatar
Thomas Telkamp committed
29
30
31
32
33
34
35

    # sigmfname_data = sigmfname_meta.replace("-meta", "-data")
    if dataformat.strip('"') == "short":
        datatype = "ci16_le"
    elif dataformat.strip('"') == "float":
        datatype = "cf32_le"
    elif dataformat.strip('"') == "double":
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
36
        datatype = "cf64_le"
Thomas Telkamp's avatar
Thomas Telkamp committed
37
38
39
40
41
42
43
44
45
46
47
48
    else:
        datatype = "unknown"

    newfilename = iqfile.replace(".iq", "") + "_" + \
        f"{frequency/1e6:.3f}MHz" + \
        f"_{rate/1e6:.1f}Msps" + \
        "_" + datatype + \
        ".sigmf-data"
    sigmfname_meta = newfilename.replace("sigmf-data", "sigmf-meta")

    meta = {}
    meta["global"] = {}
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
49
50
51
52
    global_meta = meta["global"]
    global_meta["core:description"] = "Dwingeloo Radio Telescope, PI9CAM"
    global_meta["core:version"] = "1.0.0"
    global_meta[
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
53
        "core:license"] = "https://creativecommons.org/licenses/by/4.0/"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
54
55
56
57
58
59
    global_meta["core:author"] = "Stichting CAMRAS"
    global_meta["core:recorder"] = "usrp_to_file"
    global_meta["core:dataset"] = os.path.basename(newfilename)
    global_meta["core:datatype"] = datatype
    global_meta["core:sample_rate"] = rate
    global_meta["core:hw"] = sdr_type.strip('"')
Thomas Telkamp's avatar
Thomas Telkamp committed
60
61
62
63
64
65
66

    meta["annotations"] = []

    meta["captures"] = []
    meta["captures"].append({
        "core:sample_start": 0,
        "core:frequency": frequency,
Thomas Telkamp's avatar
Thomas Telkamp committed
67
        "core:datetime": time_first_sample.datetime.isoformat(),
Thomas Telkamp's avatar
Thomas Telkamp committed
68
69
70
    })

    # CAMRAS
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
71
    try:
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
72
73
        dt = Telescope(consoleHost="console")
        time.sleep(1.1)  # Make sure everything is updated
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
74
        console_is_on = True
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
75
76
77
78
79
80
81
    except IOError:
        console_is_on = False

    if not console_is_on:
        apilink = urlopen("https://www.camras.nl/pointing.php")
        pointingdata = json.loads(apilink.read().decode())

Thomas Telkamp's avatar
Thomas Telkamp committed
82
        global_meta["camras:pointing:source"] = "https://www.camras.nl/pointing.php"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
83
84
        global_meta["camras:pointing:tracking"] = pointingdata[
            'setpoint'].strip('"')
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
85
86
        global_meta[
            "camras:pointing:az_deg"] = f"{(pointingdata['az'] + 180) % 360:.4f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
87
        global_meta["camras:pointing:el_deg"] = f"{pointingdata['el']:.4f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
88
89
90
91
92
93
94
95
96
        if pointingdata['offset'].strip() == "":
            offset_az_deg = 0
            offset_el_deg = 0
        else:
            offset_az_str, offset_el_str = pointingdata['offset'].split(',')
            offset_az_str = offset_az_str.strip().rstrip('°')
            offset_el_str = offset_el_str.strip().rstrip('°')
            global_meta["camras:pointing:offset:az_deg"] = offset_az_str
            global_meta["camras:pointing:offset:el_deg"] = offset_el_str
Thomas Telkamp's avatar
Thomas Telkamp committed
97
        global_meta["camras:pointing:datetime"] = Time.now(
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
98
        ).iso[:19]  # Pointing is only accurate to the second
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
99
    else:
Thomas Telkamp's avatar
Thomas Telkamp committed
100
        global_meta["camras:pointing:source"] = "DT controller"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
101
102
        tracker = dt.tracker.replace("console/", "")
        global_meta["camras:pointing:tracking"] = dt.tracker
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
103
104
        global_meta[
            "camras:pointing:az_deg"] = f"{(dt.az.value + 180) % 360:.4f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
105
        global_meta["camras:pointing:el_deg"] = f"{dt.el.value:.4f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
106
        global_meta[
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
107
            "camras:pointing:offset:az_deg"] = f"{dt.offset_az.value:+.4f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
108
        global_meta[
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
109
110
            "camras:pointing:offset:el_deg"] = f"{dt.offset_el.value:+.4f}"
        global_meta["camras:focusbox_position_m"] = f"{dt.focusbox_pos:.3f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
111

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
112
113
114
115
116
117
118
        global_meta["camras:pointing:datetime"] = Time.now(
        ).datetime.isoformat()

        if tracker == 'j2000tracker':
            sky = dt.getRaDec()
            global_meta["camras:pointing:ra_deg"] = f"{dt.radec.ra.deg:.4f}"
            global_meta["camras:pointing:dec_deg"] = f"{dt.radec.dec.deg:.4f}"
119
            global_meta["camras:pointing:radec_hmsdms"] = sky.to_string('hmsdms')
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
120
121
            objects = Table.read('camras_objects.ecsv')

Thomas Telkamp's avatar
Thomas Telkamp committed
122
123
            catalog = SkyCoord(objects["coord"])
            names = objects["name"]
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
124
125
126
127
128
129
130
            idx, d2d, d3d = sky.match_to_catalog_sky(catalog)
            separation = sky.separation(catalog[idx])
            if separation < 6 * u.deg:
                global_meta["camras:pointing:nearest_object"] = names[
                    idx].strip('"')
                global_meta[
                    "camras:pointing:nearest_object_separation_deg"] = f"{separation.deg:.4f}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
131
132
133
134
        elif tracker == 'sattracker':
            global_meta["camras:pointing:satname"] = dt.satname
            global_meta["camras:pointing:tle1"] = dt.tle1
            global_meta["camras:pointing:tle2"] = dt.tle2
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
135

Thomas Telkamp's avatar
Thomas Telkamp committed
136
        if tracker in ('j2000tracker', 'moontracker', 'suntracker',
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
137
138
139
140
141
                          'sattracker'):
            global_meta[
                "camras:pointing:model_enabled"] = f"{dt.model_enabled}"
            global_meta[
                "camras:pointing:refraction_enabled"] = f"{dt.refraction_enabled}"
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
142
143
144
145
146
147
148

    global_meta["camras:rx_gain"] = rx_gain
    global_meta["camras:bandwidth"] = bandwidth
    global_meta["camras:reference"] = reference.strip('"')
    global_meta["camras:time_source"] = time_source.strip('"')
    global_meta["camras:antenna"] = antenna.strip('"')
    global_meta["camras:channel"] = channel
Thomas Telkamp's avatar
Thomas Telkamp committed
149
150
151

    with open(sigmfname_meta, "w") as sigmfmeta:
        json.dump(meta, sigmfmeta, indent=4)
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
152
153
154
155
156

    if not no_rename:
        os.rename(iqfile, newfilename)
        os.remove(filename_metadata)

Thomas Telkamp's avatar
Thomas Telkamp committed
157
158
    print(sigmfname_meta)

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
159
160
161
162
163
164
165
166

def is_valid_file(parser, arg):
    if not os.path.exists(arg):
        parser.error(f"The file {arg} does not exist!")
    else:
        return arg


Thomas Telkamp's avatar
Thomas Telkamp committed
167
if __name__ == "__main__":
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
168
169
170
171
172
173
    parser = ArgumentParser(
        description="Make SIGMF metadata out of IQ dump, "
        "metadata CSV and a connection to the telescope console")
    parser.add_argument("iqfile",
                        metavar="IQ file",
                        type=lambda x: is_valid_file(parser, x))
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
174
175
176
177
178
179
    parser.add_argument(
        "-n",
        "--no-rename",
        help="Dry-run: do not rename files, just write the metadata",
        action="store_true")

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
180
181
    args = parser.parse_args()

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
182
    make_sigmf(args.iqfile, no_rename=args.no_rename)