From 22ad490aa53056f950155a602fbc5fc872753f08 Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Tue, 16 Jul 2024 06:42:07 -0400 Subject: [PATCH 1/7] Note: Squashed a bunch of commits and cleaned up commit history message Explicit int conversion removed MAXI_UNKNOWN conversion Removed GECAM_FLT and GECAM_GND conversion Added documentation and changed function name reformatted file deleted files --- .../notices/CALET_GBM_FLT_LC/__init__.py | 51 +++++++++++++++++++ .../notices/CALET_GBM_FLT_LC/example.json | 28 ++++++++++ .../notices/SWIFT_BAT_GRB_POS_ACK/__init__.py | 12 +---- .../SWIFT_BAT_GRB_POS_ACK/example.json | 2 +- gcn_classic_to_json/utils.py | 28 ++++++++++ 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py create mode 100644 gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json create mode 100644 gcn_classic_to_json/utils.py diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py new file mode 100644 index 0000000..f7445f2 --- /dev/null +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py @@ -0,0 +1,51 @@ +import numpy as np + +from ... import utils + + +def parse(bin): + if (bin[0] != 160) and (bin[39] != 10): + return + + if np.all(bin[1:4]) and bin[12] and np.all(bin[13:16]) and np.all(bin[20:29]): + pass + + lat, lon = bin[16:17].view(">i2") + + trig_id_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) + trig_id_descriptions = { + 5: "This is not a real event.\n", + 29: "There was a temporal coincidence with another event.\n", + 30: "This is a test submission.\n", + } + comments = "".join( + [val for (key, val) in trig_id_descriptions.items() if trig_id_bits[key] == 1] + ) + + detector_options = ["on", "triggered"] + detectors_bits = np.flip(np.unpackbits(bin[19:20].view(dtype="u1")))[:3] + detectors_status = [detector_options[bit] for bit in detectors_bits] + detectors = dict(zip(["HXM1", "HMX2", "SGM"], detectors_status)) + + return { + "mission": "CALET", + "instrument": "GBM", + # "alert_type" : "initial", # does not exist in the binaries + # "record_number" = 1, # does not exist in binaries + "id": [bin[4]], + "messenger": "EM", + "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6]), + "trigger_type": "rate", + "rate_energy_range": np.flip(bin[17:18].view(">i2")), + "rate_snr": bin[9] * 1e-2, + "rate_duration": bin[10] * 1e-2, + "background_duration": bin[11] * 1e-2, + "ra": bin[7] * 1e-4, + "dec": bin[8] * 1e-4, + "latitude": lat * 1e-2, + "longitude": lon * 1e-2, + "detector_status": detectors, + "url": "http://cgbm.calet.jp/cgbm_trigger/flight/" + + utils.binary_to_string(bin[29:39]), + "additional_info": comments if comments else None, + } diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json new file mode 100644 index 0000000..20e08b4 --- /dev/null +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json @@ -0,0 +1,28 @@ +{ + "mission": "CALET", + "instrument": "GBM", + "id": [ + 1399729386 + ], + "messenger": "EM", + "trigger_time": "2024-05-14T13:49:42.620Z", + "trigger_type": "rate", + "rate_energy_range": [ + 40, + 230 + ], + "rate_snr": 8.3, + "rate_duration": 4.0, + "background_duration": 16.0, + "ra": 193.85000000000002, + "dec": 50.359, + "latitude": 50.22, + "longitude": 113.87, + "detector_status": { + "HXM1": "on", + "HMX2": "on", + "SGM": "triggered" + }, + "url": "http://cgbm.calet.jp/cgbm_trigger/flight/index.html", + "additional_info": null +} diff --git a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py index c63ef56..16e9129 100644 --- a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py +++ b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py @@ -1,17 +1,9 @@ -from astropy.time import Time - -# Zero point for Truncated Julian Day according to -# https://en.wikipedia.org/wiki/Julian_day. -TJD0 = (2440000, 0.5) +from ... import utils def parse(bin): return { - "trigger_time": Time( - bin[5] + TJD0[0], - bin[6] / 8640000 + TJD0[1], - format="jd", - ).isot, + "trigger_time": utils.tjd_to_jd(bin[5], bin[6]), "ra": 1e-4 * bin[7], "dec": 1e-4 * bin[8], "ra_dec_error": 1e-4 * bin[11], diff --git a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json index 9202286..9421539 100644 --- a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json +++ b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json @@ -1,5 +1,5 @@ { - "trigger_time": "2024-05-11T18:06:53.220", + "trigger_time": "2024-05-11 18:06:53.220Z", "ra": 336.66450000000003, "dec": 8.5135, "ra_dec_error": 0.05 diff --git a/gcn_classic_to_json/utils.py b/gcn_classic_to_json/utils.py new file mode 100644 index 0000000..38c9457 --- /dev/null +++ b/gcn_classic_to_json/utils.py @@ -0,0 +1,28 @@ +from astropy.time import Time +import numpy as np + + +def datetime_to_iso8601(date, time): + """Converts time to ISO 8601 format. + + The function convert datetime into the ISO 8601 format from + Truncated Julian Date by first converting it to Julian Date. + + Parameters + ---------- + date : int + Date must be in Truncated Julian Date format. + time : int + Time of day must in Seconds of Day format. + + Returns + ------- + string + returns datetime in ISO8601 format. + + Notes + ----- + The zero point for Truncated Julian Day is given in https://en.wikipedia.org/wiki/Julian_day. + """ + TJD0 = (2440000, 0.5) + return Time(date + TJD0[0], time / 8640000 + TJD0[1], format="jd").isot + "Z" From eb667a491e9596a286a14551a2321a33107c371b Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Tue, 6 Aug 2024 14:03:55 -0400 Subject: [PATCH 2/7] reformatting --- .../notices/CALET_GBM_FLT_LC/__init__.py | 2 -- .../notices/SWIFT_BAT_GRB_POS_ACK/__init__.py | 2 +- .../SWIFT_BAT_GRB_POS_ACK/example.json | 2 +- gcn_classic_to_json/test/test_notices.py | 2 +- gcn_classic_to_json/utils.py | 31 +++++++++++++++++-- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py index f7445f2..82a8aaa 100644 --- a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py @@ -30,8 +30,6 @@ def parse(bin): return { "mission": "CALET", "instrument": "GBM", - # "alert_type" : "initial", # does not exist in the binaries - # "record_number" = 1, # does not exist in binaries "id": [bin[4]], "messenger": "EM", "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6]), diff --git a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py index 16e9129..a7c8b60 100644 --- a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py +++ b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/__init__.py @@ -3,7 +3,7 @@ def parse(bin): return { - "trigger_time": utils.tjd_to_jd(bin[5], bin[6]), + "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6]), "ra": 1e-4 * bin[7], "dec": 1e-4 * bin[8], "ra_dec_error": 1e-4 * bin[11], diff --git a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json index 9421539..d493ebb 100644 --- a/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json +++ b/gcn_classic_to_json/notices/SWIFT_BAT_GRB_POS_ACK/example.json @@ -1,5 +1,5 @@ { - "trigger_time": "2024-05-11 18:06:53.220Z", + "trigger_time": "2024-05-11T18:06:53.220Z", "ra": 336.66450000000003, "dec": 8.5135, "ra_dec_error": 0.05 diff --git a/gcn_classic_to_json/test/test_notices.py b/gcn_classic_to_json/test/test_notices.py index fb85aee..c8fa286 100644 --- a/gcn_classic_to_json/test/test_notices.py +++ b/gcn_classic_to_json/test/test_notices.py @@ -67,7 +67,7 @@ def mock_frombuffer(*args, **kwargs): if not used.all(): raise AssertionError( - f'All fields in the binary packet must be used. The fields with the following indices were unused: {' '.join(np.flatnonzero(~used).astype(str))}' + f"All fields in the binary packet must be used. The fields with the following indices were unused: {' '.join(np.flatnonzero(~used).astype(str))}" ) diff --git a/gcn_classic_to_json/utils.py b/gcn_classic_to_json/utils.py index 38c9457..362a356 100644 --- a/gcn_classic_to_json/utils.py +++ b/gcn_classic_to_json/utils.py @@ -1,11 +1,11 @@ -from astropy.time import Time import numpy as np +from astropy.time import Time def datetime_to_iso8601(date, time): """Converts time to ISO 8601 format. - The function convert datetime into the ISO 8601 format from + The function converts input into the ISO 8601 format from Truncated Julian Date by first converting it to Julian Date. Parameters @@ -26,3 +26,30 @@ def datetime_to_iso8601(date, time): """ TJD0 = (2440000, 0.5) return Time(date + TJD0[0], time / 8640000 + TJD0[1], format="jd").isot + "Z" + + +def binary_to_string(binary): + """Converts a binary array to a ASCII-string. + + The function converts each field encoded as a 4-byte integer into + four 1-byte integers and then to their corresponding ASCII value. + + Parameters + ---------- + binary : array-like + A array for binary values encoded as 4-byte integers. + + Returns + ------- + string: + returns the corresponding ASCII string. + + Notes: + ------ + The strings in the binary packets seem to encoded little-endian. + """ + bits_array = np.asarray(binary, dtype=" Date: Tue, 6 Aug 2024 17:35:30 -0400 Subject: [PATCH 3/7] Added code to ignore unused binary fields. --- .../notices/CALET_GBM_FLT_LC/__init__.py | 16 ++++++++++------ .../notices/CALET_GBM_FLT_LC/example.json | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py index 82a8aaa..5b1666d 100644 --- a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py @@ -4,11 +4,15 @@ def parse(bin): - if (bin[0] != 160) and (bin[39] != 10): - return - - if np.all(bin[1:4]) and bin[12] and np.all(bin[13:16]) and np.all(bin[20:29]): - pass + assert bin[12] == 0, "Unused. According to docs: 'Always 0 for FLT_LC'" + bin[ + 1 + ] # Unused. According to docs: 'serial number of the packet'. Generally set to 1. + bin[2] # Unused. According to docs: 'hopcount item is defunct'. + bin[ + 3 + ] # Unused. According to docs: 'seconds of day when packet was created'. Cannot use without date that the packet. + bin[20:29] # Unused. According to docs: '36 bytes for the future' lat, lon = bin[16:17].view(">i2") @@ -43,7 +47,7 @@ def parse(bin): "latitude": lat * 1e-2, "longitude": lon * 1e-2, "detector_status": detectors, - "url": "http://cgbm.calet.jp/cgbm_trigger/flight/" + "url": f"http://cgbm.calet.jp/cgbm_trigger/flight/{bin[4]}" + utils.binary_to_string(bin[29:39]), "additional_info": comments if comments else None, } diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json index 20e08b4..faf6ddd 100644 --- a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json @@ -23,6 +23,6 @@ "HMX2": "on", "SGM": "triggered" }, - "url": "http://cgbm.calet.jp/cgbm_trigger/flight/index.html", + "url": "http://cgbm.calet.jp/cgbm_trigger/flight/1399729386index.html", "additional_info": null } From 995b03ebb52b19519343f7880637311bbf4b5df9 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Tue, 6 Aug 2024 17:46:21 -0400 Subject: [PATCH 4/7] fixed URL and refactored utils.py --- .../notices/CALET_GBM_FLT_LC/__init__.py | 11 ++++------- .../notices/CALET_GBM_FLT_LC/example.json | 2 +- gcn_classic_to_json/utils.py | 19 +++++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py index 5b1666d..5229dae 100644 --- a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py @@ -5,13 +5,10 @@ def parse(bin): assert bin[12] == 0, "Unused. According to docs: 'Always 0 for FLT_LC'" - bin[ - 1 - ] # Unused. According to docs: 'serial number of the packet'. Generally set to 1. + bin[1] # Unused. According to docs: 'Generally set to 1.' bin[2] # Unused. According to docs: 'hopcount item is defunct'. - bin[ - 3 - ] # Unused. According to docs: 'seconds of day when packet was created'. Cannot use without date that the packet. + bin[3] # Unused. According to docs: 'seconds of day when packet was created'. + bin[13:16] # Unused. According to docs: '12 bytes for the future' bin[20:29] # Unused. According to docs: '36 bytes for the future' lat, lon = bin[16:17].view(">i2") @@ -47,7 +44,7 @@ def parse(bin): "latitude": lat * 1e-2, "longitude": lon * 1e-2, "detector_status": detectors, - "url": f"http://cgbm.calet.jp/cgbm_trigger/flight/{bin[4]}" + "url": f"http://cgbm.calet.jp/cgbm_trigger/flight/{bin[4]}/" + utils.binary_to_string(bin[29:39]), "additional_info": comments if comments else None, } diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json index faf6ddd..2e26abf 100644 --- a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/example.json @@ -23,6 +23,6 @@ "HMX2": "on", "SGM": "triggered" }, - "url": "http://cgbm.calet.jp/cgbm_trigger/flight/1399729386index.html", + "url": "http://cgbm.calet.jp/cgbm_trigger/flight/1399729386/index.html", "additional_info": null } diff --git a/gcn_classic_to_json/utils.py b/gcn_classic_to_json/utils.py index 362a356..f643ed8 100644 --- a/gcn_classic_to_json/utils.py +++ b/gcn_classic_to_json/utils.py @@ -31,8 +31,9 @@ def datetime_to_iso8601(date, time): def binary_to_string(binary): """Converts a binary array to a ASCII-string. - The function converts each field encoded as a 4-byte integer into - four 1-byte integers and then to their corresponding ASCII value. + The function converts `binary` into a C-style string, + flips the position of every 4 bytes, strips excess null characters + and then converts the result into ASCII characters. Parameters ---------- @@ -46,10 +47,12 @@ def binary_to_string(binary): Notes: ------ - The strings in the binary packets seem to encoded little-endian. + The strings in the binary packets look like they were accidentally byte-swapped. """ - bits_array = np.asarray(binary, dtype=" Date: Wed, 7 Aug 2024 12:00:16 -0400 Subject: [PATCH 5/7] Moved pkt_sernum, pkt_hop_cnt and pkt_sod to notices/__init__.py --- gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py | 3 --- gcn_classic_to_json/notices/__init__.py | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py index 5229dae..a8bfd25 100644 --- a/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py +++ b/gcn_classic_to_json/notices/CALET_GBM_FLT_LC/__init__.py @@ -5,9 +5,6 @@ def parse(bin): assert bin[12] == 0, "Unused. According to docs: 'Always 0 for FLT_LC'" - bin[1] # Unused. According to docs: 'Generally set to 1.' - bin[2] # Unused. According to docs: 'hopcount item is defunct'. - bin[3] # Unused. According to docs: 'seconds of day when packet was created'. bin[13:16] # Unused. According to docs: '12 bytes for the future' bin[20:29] # Unused. According to docs: '36 bytes for the future' diff --git a/gcn_classic_to_json/notices/__init__.py b/gcn_classic_to_json/notices/__init__.py index 3185c64..a7d388b 100644 --- a/gcn_classic_to_json/notices/__init__.py +++ b/gcn_classic_to_json/notices/__init__.py @@ -20,6 +20,9 @@ def parse(key, value): ints = _frombuffer(value) assert len(ints) == 40 assert ints[0] == NoticeType[key], "Field 0 must equal the notice type" + ints[1] # Unused. According to docs: 'Generally set to 1.' + ints[2] # Unused. According to docs: 'hopcount item is defunct'. + ints[3] # Unused. According to docs: 'seconds of day when packet was created'. assert ( ints[-1] == np.asarray("\0\0\0\n", dtype="c").view(">i4")[0] ), "Field 39 must be a newline" From cb7721fd0b9e1d594967f4c1fc08ee19f3e13ec1 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Thu, 15 Aug 2024 19:43:10 -0400 Subject: [PATCH 6/7] LVC_INITIAL, LVC_UPDATE, LVC_PRELIMINARY conversions --- .../notices/LVC_INITIAL/__init__.py | 11 ++ .../notices/LVC_INITIAL/example.json | 43 +++++ .../notices/LVC_PRELIMINARY/__init__.py | 159 ++++++++++++++++++ .../notices/LVC_PRELIMINARY/example.json | 39 +++++ .../notices/LVC_UPDATE/__init__.py | 11 ++ .../notices/LVC_UPDATE/example.json | 43 +++++ 6 files changed, 306 insertions(+) create mode 100644 gcn_classic_to_json/notices/LVC_INITIAL/__init__.py create mode 100644 gcn_classic_to_json/notices/LVC_INITIAL/example.json create mode 100644 gcn_classic_to_json/notices/LVC_PRELIMINARY/__init__.py create mode 100644 gcn_classic_to_json/notices/LVC_PRELIMINARY/example.json create mode 100644 gcn_classic_to_json/notices/LVC_UPDATE/__init__.py create mode 100644 gcn_classic_to_json/notices/LVC_UPDATE/example.json diff --git a/gcn_classic_to_json/notices/LVC_INITIAL/__init__.py b/gcn_classic_to_json/notices/LVC_INITIAL/__init__.py new file mode 100644 index 0000000..e7df870 --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_INITIAL/__init__.py @@ -0,0 +1,11 @@ +from ..LVC_PRELIMINARY import parse as parse_lvc_prelim + + +def parse(bin): + property_bytes = bin[20:21].view(dtype=">u2") + prob_ns, prob_remnant = property_bytes * 1e-2 + return { + **parse_lvc_prelim(bin), + "alert_type": "initial", + "properties": ({"NS": prob_ns}, {"REMNANT": prob_remnant}), + } diff --git a/gcn_classic_to_json/notices/LVC_INITIAL/example.json b/gcn_classic_to_json/notices/LVC_INITIAL/example.json new file mode 100644 index 0000000..9bef814 --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_INITIAL/example.json @@ -0,0 +1,43 @@ +{ + "mission": "LVC", + "alert_tense": "test", + "messenger": "GW", + "trigger_time": "2024-05-11T22:40:30.203Z", + "id": [ + "MS240511w" + ], + "record_number": 3, + "fluence": null, + "peak_frequency": null, + "far": 9.111712330429193e-14, + "pipeline_type": "GSTLAL", + "search_type": "MockDataChallenge", + "group_type": "CBC", + "duration": null, + "p_astro": 1.0, + "classification": [ + { + "TERRESTRIAL": 0.0 + }, + { + "BBH": 0.0 + }, + { + "BNS": 1.0 + }, + { + "NSBH": 0.0 + } + ], + "properties": [ + { + "NS": 1.0 + }, + { + "REMNANT": 1.0 + } + ], + "skymap_url": "https://gracedb.ligo.org/superevents/MS240511w/files/bayestar.multiorder.fit", + "additional_info": "This is a test Notice.\nThis event is an Open Alert.\nLIGO-Handford Observatory contributed to this candidate event.\nLIGO-Livingston Observatory contributed to this candidate event.\nThis Notice was ground-generated.\n", + "alert_type": "initial" +} diff --git a/gcn_classic_to_json/notices/LVC_PRELIMINARY/__init__.py b/gcn_classic_to_json/notices/LVC_PRELIMINARY/__init__.py new file mode 100644 index 0000000..0269a64 --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_PRELIMINARY/__init__.py @@ -0,0 +1,159 @@ +import numpy as np + +from ... import utils + +pipeline_opts = [ + "undefined/illegal", + "MTBAOnline", + "CWB", + "CWB2G", + "GSTLAL", + "GSTLAL_Spiir", + "Hardware Injection", + "X", + "Q", + "Omega", + "Ringdown", + "LIB", + "Fermi", + "Swift", + "SNEWS", + "pycbc", +] + +search_opts = [ + "undefined/illegal", + "AllSky", + "LowMass", + "HighMass", + "GRB", + "Supernova", + "MockDataChallenge", + "AllSkyLong", + "BBH", +] + +group_opts = ["undefined/illegal", "CBC", "Burst", "Test"] + +trig_id_description = { + 1: "This is a test Notice.\n", + 2: "This is a hardware injection event.\n", + 3: "This event has been vetted by a human.\n", + 4: "This event is an Open Alert.\n", + 5: "This is definitely a retraction.\n", + 29: "There was a temporal coincidence with another event.\n", +} + +misc_descriptions = { + 0: "LIGO-Handford Observatory contributed to this candidate event.\n", + 1: "LIGO-Livingston Observatory contributed to this candidate event.\n", + 2: "Virgo Observatory contributed to this candidate event.\n", + 3: "GEO600 Observatory contributed to this candidate event.\n", + 4: "KAGRA Observatory contributed to this candidate event.\n", + 5: "LIGO-India Observaiory contributed to this candidate event.\n", +} + +prefix_letters = [ + "G", + "T", + "M", + "Y", + "H", + "E", + "K", + "S", + "GW", + "TS", + "TGW", + "MS", + "MGW", +] + +invalid_prob_descriptions = { + 0: "Probability of NS is invalid.\n", + 1: "Probability of REMNANT is invalid.\n", + 2: "Probability of BNS is invalid.\n", + 3: "Probability of NSBH is invalid.\n", + 4: "Probability of BBH is invalid.\n", + 5: "Probability of Mass Gap is invalid.\n", + 6: "Probability of TERRESTRIAL is invalid.\n", +} + + +def parse(bin): + bin[7:9] # Intentionally Omitted. RA/Dec is set to zero due to large localization. + bin[15:18] # Spare. According to Docs: '12 bytes for the future' + bin[20] # Unused. According to Docs: Not present in preliminary notice type. + bin[24:29] # Spare. According to Docs: '20 bytes for the future' + + event_type_bytes = np.flip(bin[12:13].view(dtype="u1")) + pipeline_num, search_num, group_num, _ = event_type_bytes + + fluence = 10 ** (bin[9] * 1e-4) * 1e3 + peak_freq = bin[10] * 1e-2 + duration = bin[14] * 1e-2 + + trig_id_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) + + misc_bits = np.flip(np.unpackbits(bin[19:20].view(dtype="u1"))) + + first_suffix = chr(np.packbits(np.flip(misc_bits[10:18]))[0]) + prefix = prefix_letters[ + np.packbits(np.pad(np.flip(misc_bits[20:24]), (4, 0)))[0] - 1 + ] + seq_num = np.packbits(np.flip(misc_bits[24:32]))[0] + + mass_gap_and_letters_bytes = np.flip(bin[21:22].view(dtype="u1")) + second_suffix = chr(np.packbits(mass_gap_and_letters_bytes[0])[0]) + prob_mass_gap = mass_gap_and_letters_bytes[3] * 1e-2 + + class_prob_bytes = bin[22:23].view(dtype="u1") + prob_bns, prob_nsbh, prob_bbh, prob_terrestrial = class_prob_bytes * 1e-2 + + prob_invalid_flags_bits = np.flip(np.unpackbits(bin[23:24].view(dtype="u1"))) + + comments = "" + comments += "".join( + [val for (key, val) in trig_id_description.items() if trig_id_bits[key]] + ) + comments += "".join( + [val for (key, val) in misc_descriptions.items() if misc_bits[key]] + ) + comments += ( + "This Notice was ground-generated.\n" + if misc_bits[19] + else "This Notice was flight-generated.\n" + ) + comments += "".join( + [ + val + for (key, val) in invalid_prob_descriptions.items() + if prob_invalid_flags_bits[key] + ] + ) + + return { + "mission": "LVC", + "alert_tense": "test" if trig_id_bits[1] else "current", + "messenger": "GW", + "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6] + (bin[13] * 1e-4)), + "id": [(prefix + str(bin[4]) + first_suffix + second_suffix).strip("\u0000")], + "record_number": seq_num, + "fluence": fluence if group_num == 2 else None, + "peak_frequency": peak_freq if group_num == 2 else None, + "far": 10 ** (bin[11] * 1e-4), + "pipeline_type": pipeline_opts[pipeline_num], + "search_type": search_opts[search_num], + "group_type": group_opts[group_num], + "duration": duration if group_num == 2 else None, + "p_astro": 1 - prob_terrestrial, + "classification": ( + {"TERRESTRIAL": prob_terrestrial}, + {"BBH": prob_bbh}, + {"BNS": prob_bns}, + {"NSBH": prob_nsbh}, + ), + "properties": ({"Mass Gap": prob_mass_gap},), + "skymap_url": f"https://gracedb.ligo.org/superevents/{utils.binary_to_string(bin[29:39])}", + "additional_info": comments if comments else None, + } diff --git a/gcn_classic_to_json/notices/LVC_PRELIMINARY/example.json b/gcn_classic_to_json/notices/LVC_PRELIMINARY/example.json new file mode 100644 index 0000000..5f9f8a6 --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_PRELIMINARY/example.json @@ -0,0 +1,39 @@ +{ + "mission": "LVC", + "alert_tense": "test", + "messenger": "GW", + "trigger_time": "2024-05-10T05:47:17.764Z", + "id": [ + "MS240510f" + ], + "record_number": 1, + "fluence": null, + "peak_frequency": null, + "far": 9.31536765238572e-16, + "pipeline_type": "GSTLAL", + "search_type": "MockDataChallenge", + "group_type": "CBC", + "duration": null, + "p_astro": 1.0, + "classification": [ + { + "TERRESTRIAL": 0.0 + }, + { + "BBH": 0.0 + }, + { + "BNS": 1.0 + }, + { + "NSBH": 0.0 + } + ], + "properties": [ + { + "Mass Gap": 0.0 + } + ], + "skymap_url": "https://gracedb.ligo.org/superevents/MS240510f/files/bayestar.multiorder.fit", + "additional_info": "This is a test Notice.\nThis event is an Open Alert.\nLIGO-Handford Observatory contributed to this candidate event.\nLIGO-Livingston Observatory contributed to this candidate event.\nVirgo Observatory contributed to this candidate event.\nThis Notice was ground-generated.\n" +} diff --git a/gcn_classic_to_json/notices/LVC_UPDATE/__init__.py b/gcn_classic_to_json/notices/LVC_UPDATE/__init__.py new file mode 100644 index 0000000..1dc7b78 --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_UPDATE/__init__.py @@ -0,0 +1,11 @@ +from ..LVC_PRELIMINARY import parse as parse_lvc_prelim + + +def parse(bin): + property_bytes = bin[20:21].view(dtype=">u2") + prob_ns, prob_remnant = property_bytes * 1e-2 + return { + **parse_lvc_prelim(bin), + "alert_type": "update", + "properties": ({"NS": prob_ns}, {"REMNANT": prob_remnant}), + } diff --git a/gcn_classic_to_json/notices/LVC_UPDATE/example.json b/gcn_classic_to_json/notices/LVC_UPDATE/example.json new file mode 100644 index 0000000..33164d5 --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_UPDATE/example.json @@ -0,0 +1,43 @@ +{ + "mission": "LVC", + "alert_tense": "current", + "messenger": "GW", + "trigger_time": "2024-05-14T12:17:13.721Z", + "id": [ + "S240514x" + ], + "record_number": 4, + "fluence": null, + "peak_frequency": null, + "far": 3.1681081593280504e-10, + "pipeline_type": "pycbc", + "search_type": "AllSky", + "group_type": "CBC", + "duration": null, + "p_astro": 1.0, + "classification": [ + { + "TERRESTRIAL": 0.0 + }, + { + "BBH": 1.0 + }, + { + "BNS": 0.0 + }, + { + "NSBH": 0.0 + } + ], + "properties": [ + { + "NS": 0.0 + }, + { + "REMNANT": 0.0 + } + ], + "skymap_url": "https://gracedb.ligo.org/superevents/S240514x/files/Bilby.multiorder.fits,0", + "additional_info": "This event is an Open Alert.\nLIGO-Handford Observatory contributed to this candidate event.\nLIGO-Livingston Observatory contributed to this candidate event.\nVirgo Observatory contributed to this candidate event.\nThis Notice was ground-generated.\n", + "alert_type": "update" +} From 325484e7447d948dee15bb98302fe58f835de1f9 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Fri, 16 Aug 2024 11:06:36 -0400 Subject: [PATCH 7/7] LVC_RETRACTION conversion --- .../notices/LVC_RETRACTION/__init__.py | 114 ++++++++++++++++++ .../notices/LVC_RETRACTION/example.json | 13 ++ 2 files changed, 127 insertions(+) create mode 100644 gcn_classic_to_json/notices/LVC_RETRACTION/__init__.py create mode 100644 gcn_classic_to_json/notices/LVC_RETRACTION/example.json diff --git a/gcn_classic_to_json/notices/LVC_RETRACTION/__init__.py b/gcn_classic_to_json/notices/LVC_RETRACTION/__init__.py new file mode 100644 index 0000000..51d887c --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_RETRACTION/__init__.py @@ -0,0 +1,114 @@ +import numpy as np + +from ... import utils + +pipeline_opts = [ + "undefined/illegal", + "MTBAOnline", + "CWB", + "CWB2G", + "GSTLAL", + "GSTLAL_Spiir", + "Hardware Injection", + "X", + "Q", + "Omega", + "Ringdown", + "LIB", + "Fermi", + "Swift", + "SNEWS", + "pycbc", +] + +search_opts = [ + "undefined/illegal", + "AllSky", + "LowMass", + "HighMass", + "GRB", + "Supernova", + "MockDataChallenge", + "AllSkyLong", + "BBH", +] + +group_opts = ["undefined/illegal", "CBC", "Burst", "Test"] + +trig_id_description = { + 1: "This is a test Notice.\n", + 2: "This is a hardware injection event.\n", + 3: "This event has been vetted by a human.\n", + 4: "This event is an Open Alert.\n", + 5: "This is definitely a retraction.\n", + 29: "There was a temporal coincidence with another event.\n", +} + +misc_descriptions = { + 0: "LIGO-Handford Observatory contributed to this candidate event.\n", + 1: "LIGO-Livingston Observatory contributed to this candidate event.\n", + 2: "Virgo Observatory contributed to this candidate event.\n", + 3: "GEO600 Observatory contributed to this candidate event.\n", + 4: "KAGRA Observatory contributed to this candidate event.\n", + 5: "LIGO-India Observaiory contributed to this candidate event.\n", +} + +prefix_letters = [ + "G", + "T", + "M", + "Y", + "H", + "E", + "K", + "S", + "GW", + "TS", + "TGW", + "MS", + "MGW", +] + + +def parse(bin): + bin[7:13] # Spare. According to Docs:'24 bytes for the future' + bin[14:18] # Spare. According to Docs:'16 bytes for the future' + bin[20:29] # Spare. According to Docs:'36 bytes for the future' + + trig_id_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) + + misc_bits = np.flip(np.unpackbits(bin[19:20].view(dtype="u1"))) + + first_suffix = chr(np.packbits(np.flip(misc_bits[10:18]))[0]) + prefix = prefix_letters[ + np.packbits(np.pad(np.flip(misc_bits[20:24]), (4, 0)))[0] - 1 + ] + seq_num = np.packbits(np.flip(misc_bits[24:32]))[0] + + letter_bytes = np.flip(bin[21:22].view(dtype="u1")) + second_suffix = chr(np.packbits(letter_bytes[0])[0]) + + comments = "" + comments += "".join( + [val for (key, val) in trig_id_description.items() if trig_id_bits[key]] + ) + comments += "".join( + [val for (key, val) in misc_descriptions.items() if misc_bits[key]] + ) + comments += ( + "This Notice was ground-generated.\n" + if misc_bits[19] + else "This Notice was flight-generated.\n" + ) + + return { + "mission": "LVC", + "alert_tense": "test" if trig_id_bits[1] else "current", + "messenger": "GW", + "alert_type": "retraction", + "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6] + (bin[13] * 1e-4)), + "id": [(prefix + str(bin[4]) + first_suffix + second_suffix).strip("\u0000")], + "record_number": seq_num, + "skymap_url": f"https://gracedb.ligo.org/superevents/{utils.binary_to_string(bin[29:39])}", + "additional_info": comments if comments else None, + } diff --git a/gcn_classic_to_json/notices/LVC_RETRACTION/example.json b/gcn_classic_to_json/notices/LVC_RETRACTION/example.json new file mode 100644 index 0000000..fc9949d --- /dev/null +++ b/gcn_classic_to_json/notices/LVC_RETRACTION/example.json @@ -0,0 +1,13 @@ +{ + "mission": "LVC", + "alert_tense": "test", + "messenger": "GW", + "alert_type": "retraction", + "trigger_time": "2024-05-11T08:38:21.988Z", + "id": [ + "MS240511i" + ], + "record_number": 3, + "skymap_url": "https://gracedb.ligo.org/superevents/", + "additional_info": "This is a test Notice.\nThis event is an Open Alert.\nThis is definitely a retraction.\nThis Notice was ground-generated.\n" +}