From 22ad490aa53056f950155a602fbc5fc872753f08 Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Tue, 16 Jul 2024 06:42:07 -0400 Subject: [PATCH 01/12] 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 02/12] 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 03/12] 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 04/12] 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 05/12] 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 26433a87fd821993374b809eef14cd2b4195f688 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Wed, 7 Aug 2024 13:43:35 -0400 Subject: [PATCH 06/12] GECAM binary conversions --- .../notices/GECAM_FLT/__init__.py | 39 +++++++++++++++++++ .../notices/GECAM_FLT/example.json | 25 ++++++++++++ .../notices/GECAM_GND/__init__.py | 32 +++++++++++++++ .../notices/GECAM_GND/example.json | 20 ++++++++++ 4 files changed, 116 insertions(+) create mode 100644 gcn_classic_to_json/notices/GECAM_FLT/__init__.py create mode 100644 gcn_classic_to_json/notices/GECAM_FLT/example.json create mode 100644 gcn_classic_to_json/notices/GECAM_GND/__init__.py create mode 100644 gcn_classic_to_json/notices/GECAM_GND/example.json diff --git a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py new file mode 100644 index 0000000..1f15a87 --- /dev/null +++ b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py @@ -0,0 +1,39 @@ +from ... import utils + + +def parse(bin): + bin[15] # Unused. According to docs: '4 bytes for the future' + bin[27:39] # Unused. According to docs: '32 bytes for the future' + bin[ + 11 + ] # Temporarily Unused. Should have a int->class_type mapping but incomplete documentation + bin[ + 18 + ] # Temporarily Unused. Should have a bit->flag mapping but incomplete documenation + bin[21] # Temporarily Unused. How is it different from burst_dur? + bin[22:24] # Unused. Equivalent to rate_energy_range + bin[ + 26 + ] # Temporarily Unused. Presumably which detectors activated but incomplete documenation + return { + "mission": "GECAM", + "id": [bin[4]], + "messenger": "EM", + "mission_type": chr(bin[19] + 64), # mission type is A or B + # There is an error here; You are supposed to divide by 8640000 to get the correct value + # But it only seems to work if you divide by 864000000; I'm assuming it's some error in encoding the packets + "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6] * 1e-2), + "trigger_type": "rate", + "rate_energy_range": [bin[24], bin[25]], + "rate_snr": bin[20] * 1e-2, + "net_count_rate": int(bin[9]), + "rate_duration": bin[14] * 1e-4, + "ra": bin[7] * 1e-4, + "dec": bin[8] * 1e-4, + "ra_dec_error": bin[10] * 1e-4, + "containment_probability": 0.68, + "instrument_phi": bin[12] * 1e-2, + "instrument_theta": bin[13] * 1e-2, + "latitude": bin[16] * 1e-2, + "longitude": bin[17] * 1e-2, + } diff --git a/gcn_classic_to_json/notices/GECAM_FLT/example.json b/gcn_classic_to_json/notices/GECAM_FLT/example.json new file mode 100644 index 0000000..4588eca --- /dev/null +++ b/gcn_classic_to_json/notices/GECAM_FLT/example.json @@ -0,0 +1,25 @@ +{ + "mission": "GECAM", + "id": [ + 368 + ], + "messenger": "EM", + "mission_type": "B", + "trigger_time": "2024-05-17T13:01:22.550Z", + "trigger_type": "rate", + "rate_energy_range": [ + 30, + 140 + ], + "rate_snr": 5.09, + "net_count_rate": 550, + "rate_duration": 10.0, + "ra": 57.830000000000005, + "dec": 17.400000000000002, + "ra_dec_error": 1.4899, + "containment_probability": 0.68, + "instrument_phi": 7.46, + "instrument_theta": 51.27, + "latitude": 11.09, + "longitude": -8.55 +} diff --git a/gcn_classic_to_json/notices/GECAM_GND/__init__.py b/gcn_classic_to_json/notices/GECAM_GND/__init__.py new file mode 100644 index 0000000..2384443 --- /dev/null +++ b/gcn_classic_to_json/notices/GECAM_GND/__init__.py @@ -0,0 +1,32 @@ +from ... import utils + + +def parse(bin): + bin[15] # Unused. According to docs: '4 bytes for the future' + bin[20:39] # Unused. According to docs: '76 bytes for the future' + bin[ + 11 + ] # Temporarily Unused. Should have a int->class_type mapping but incomplete documentation + bin[ + 18 + ] # Temporarily Unused. Should have a bit->flag mapping but incomplete documenation + return { + "mission": "GECAM", + "id": [bin[4]], + "messenger": "EM", + "mission_type": chr(bin[19] + 64), + # There is an error here; You are supposed to divide by 8640000 to get the correct value + # But it only seems to work if you divide by 864000000; I'm assuming it's some error in encoding the packets + "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6] * 1e-2), + "trigger_type": "rate", + "net_count_rate": bin[9], + "rate_duration": bin[14] * 1e-4, + "ra": bin[7] * 1e-4, + "dec": bin[8] * 1e-4, + "ra_dec_error": bin[10] * 1e-4, + "containment_probability": 0.68, + "instrument_phi": bin[12] * 1e-2, + "instrument_theta": bin[13] * 1e-2, + "latitude": bin[16] * 1e-2, + "longitude": bin[17] * 1e-2, + } diff --git a/gcn_classic_to_json/notices/GECAM_GND/example.json b/gcn_classic_to_json/notices/GECAM_GND/example.json new file mode 100644 index 0000000..a6ed938 --- /dev/null +++ b/gcn_classic_to_json/notices/GECAM_GND/example.json @@ -0,0 +1,20 @@ +{ + "mission": "GECAM", + "id": [ + 368 + ], + "messenger": "EM", + "mission_type": "B", + "trigger_time": "2024-05-17T13:01:22.550Z", + "trigger_type": "rate", + "net_count_rate": 1306, + "rate_duration": 27.0, + "ra": 56.25, + "dec": 16.21, + "ra_dec_error": 1.57, + "containment_probability": 0.68, + "instrument_phi": 5.98, + "instrument_theta": 52.83, + "latitude": 11.09, + "longitude": -8.55 +} From d735fce8622f9379ff581ea135ea52adbd71d5cf Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Tue, 13 Aug 2024 11:58:50 -0400 Subject: [PATCH 07/12] Added temporarirly unused binaries; reformmated GECAM_FLT and calculated containment probability --- .../notices/GECAM_FLT/__init__.py | 43 +++++------------ .../notices/GECAM_FLT/example.json | 47 ++++++++++++++++--- .../notices/GECAM_GND/__init__.py | 23 ++++++--- .../notices/GECAM_GND/example.json | 10 +++- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py index 1f15a87..f8f4de8 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py @@ -1,39 +1,22 @@ -from ... import utils +import numpy as np + +from ..GECAM_GND import parse as parse_gecam_gnd def parse(bin): bin[15] # Unused. According to docs: '4 bytes for the future' bin[27:39] # Unused. According to docs: '32 bytes for the future' - bin[ - 11 - ] # Temporarily Unused. Should have a int->class_type mapping but incomplete documentation - bin[ - 18 - ] # Temporarily Unused. Should have a bit->flag mapping but incomplete documenation - bin[21] # Temporarily Unused. How is it different from burst_dur? + bin[21] # Temporarily Unused. This is trigger_duration; currently no keyword bin[22:24] # Unused. Equivalent to rate_energy_range - bin[ - 26 - ] # Temporarily Unused. Presumably which detectors activated but incomplete documenation + + detector_options = ["on", "triggered"] + detector_bits = np.flip(np.unpackbits(bin[26:27].view(dtype="u1"))) + detector_status = [detector_options[bit] for bit in detector_bits[:25]] + detectors = dict(zip(np.arange(25.0) + 1, detector_status)) + return { - "mission": "GECAM", - "id": [bin[4]], - "messenger": "EM", - "mission_type": chr(bin[19] + 64), # mission type is A or B - # There is an error here; You are supposed to divide by 8640000 to get the correct value - # But it only seems to work if you divide by 864000000; I'm assuming it's some error in encoding the packets - "trigger_time": utils.datetime_to_iso8601(bin[5], bin[6] * 1e-2), - "trigger_type": "rate", - "rate_energy_range": [bin[24], bin[25]], + **parse_gecam_gnd(bin), "rate_snr": bin[20] * 1e-2, - "net_count_rate": int(bin[9]), - "rate_duration": bin[14] * 1e-4, - "ra": bin[7] * 1e-4, - "dec": bin[8] * 1e-4, - "ra_dec_error": bin[10] * 1e-4, - "containment_probability": 0.68, - "instrument_phi": bin[12] * 1e-2, - "instrument_theta": bin[13] * 1e-2, - "latitude": bin[16] * 1e-2, - "longitude": bin[17] * 1e-2, + "rate_energy_range": [bin[24], bin[25]], + "detector_status": detectors, } diff --git a/gcn_classic_to_json/notices/GECAM_FLT/example.json b/gcn_classic_to_json/notices/GECAM_FLT/example.json index 4588eca..3eb1569 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/example.json +++ b/gcn_classic_to_json/notices/GECAM_FLT/example.json @@ -7,19 +7,52 @@ "mission_type": "B", "trigger_time": "2024-05-17T13:01:22.550Z", "trigger_type": "rate", - "rate_energy_range": [ - 30, - 140 - ], - "rate_snr": 5.09, "net_count_rate": 550, "rate_duration": 10.0, "ra": 57.830000000000005, "dec": 17.400000000000002, "ra_dec_error": 1.4899, - "containment_probability": 0.68, + "containment_probability": 0.6826894921370859, "instrument_phi": 7.46, "instrument_theta": 51.27, "latitude": 11.09, - "longitude": -8.55 + "longitude": -8.55, + "classification": [ + { + "SFLARE": 1 + } + ], + "additional_info": null, + "rate_snr": 5.09, + "rate_energy_range": [ + 30, + 140 + ], + "detector_status": { + "1.0": "triggered", + "2.0": "on", + "3.0": "on", + "4.0": "on", + "5.0": "on", + "6.0": "on", + "7.0": "on", + "8.0": "on", + "9.0": "on", + "10.0": "on", + "11.0": "triggered", + "12.0": "on", + "13.0": "on", + "14.0": "on", + "15.0": "on", + "16.0": "on", + "17.0": "triggered", + "18.0": "on", + "19.0": "on", + "20.0": "on", + "21.0": "on", + "22.0": "on", + "23.0": "on", + "24.0": "on", + "25.0": "on" + } } diff --git a/gcn_classic_to_json/notices/GECAM_GND/__init__.py b/gcn_classic_to_json/notices/GECAM_GND/__init__.py index 2384443..d93383b 100644 --- a/gcn_classic_to_json/notices/GECAM_GND/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_GND/__init__.py @@ -1,15 +1,19 @@ +import numpy as np +from scipy.stats import norm + from ... import utils +src_class_vals = ["GRB", "SFLARE", "KNOWN_SOURCE", "GENERIC_SOURCE"] + def parse(bin): bin[15] # Unused. According to docs: '4 bytes for the future' bin[20:39] # Unused. According to docs: '76 bytes for the future' - bin[ - 11 - ] # Temporarily Unused. Should have a int->class_type mapping but incomplete documentation - bin[ - 18 - ] # Temporarily Unused. Should have a bit->flag mapping but incomplete documenation + + soln_status_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) + + containment_prob = norm().cdf(1) - norm.cdf(-1) + return { "mission": "GECAM", "id": [bin[4]], @@ -24,9 +28,14 @@ def parse(bin): "ra": bin[7] * 1e-4, "dec": bin[8] * 1e-4, "ra_dec_error": bin[10] * 1e-4, - "containment_probability": 0.68, + "containment_probability": containment_prob, "instrument_phi": bin[12] * 1e-2, "instrument_theta": bin[13] * 1e-2, "latitude": bin[16] * 1e-2, "longitude": bin[17] * 1e-2, + "classification": ({src_class_vals[bin[11] - 1]: 1},), + # "classification": ({str(bin[11]) : 1}, ), + "additional_info": ( + "This is a test notice." if soln_status_bits[0] == 1 else None + ), } diff --git a/gcn_classic_to_json/notices/GECAM_GND/example.json b/gcn_classic_to_json/notices/GECAM_GND/example.json index a6ed938..6d4eab4 100644 --- a/gcn_classic_to_json/notices/GECAM_GND/example.json +++ b/gcn_classic_to_json/notices/GECAM_GND/example.json @@ -12,9 +12,15 @@ "ra": 56.25, "dec": 16.21, "ra_dec_error": 1.57, - "containment_probability": 0.68, + "containment_probability": 0.6826894921370859, "instrument_phi": 5.98, "instrument_theta": 52.83, "latitude": 11.09, - "longitude": -8.55 + "longitude": -8.55, + "classification": [ + { + "SFLARE": 1 + } + ], + "additional_info": null } From 6b26b70cb0ca661723233e71b27d687ccb2634a6 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Tue, 13 Aug 2024 13:30:07 -0400 Subject: [PATCH 08/12] reformatted code --- gcn_classic_to_json/notices/GECAM_FLT/__init__.py | 2 +- gcn_classic_to_json/notices/GECAM_GND/__init__.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py index f8f4de8..7f57834 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py @@ -12,7 +12,7 @@ def parse(bin): detector_options = ["on", "triggered"] detector_bits = np.flip(np.unpackbits(bin[26:27].view(dtype="u1"))) detector_status = [detector_options[bit] for bit in detector_bits[:25]] - detectors = dict(zip(np.arange(25.0) + 1, detector_status)) + detectors = dict(zip(list(np.arange(25.0) + 1), detector_status)) return { **parse_gecam_gnd(bin), diff --git a/gcn_classic_to_json/notices/GECAM_GND/__init__.py b/gcn_classic_to_json/notices/GECAM_GND/__init__.py index d93383b..3868cdb 100644 --- a/gcn_classic_to_json/notices/GECAM_GND/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_GND/__init__.py @@ -5,6 +5,8 @@ src_class_vals = ["GRB", "SFLARE", "KNOWN_SOURCE", "GENERIC_SOURCE"] +containment_prob = norm().cdf(1) - norm.cdf(-1) + def parse(bin): bin[15] # Unused. According to docs: '4 bytes for the future' @@ -12,8 +14,6 @@ def parse(bin): soln_status_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) - containment_prob = norm().cdf(1) - norm.cdf(-1) - return { "mission": "GECAM", "id": [bin[4]], @@ -34,7 +34,6 @@ def parse(bin): "latitude": bin[16] * 1e-2, "longitude": bin[17] * 1e-2, "classification": ({src_class_vals[bin[11] - 1]: 1},), - # "classification": ({str(bin[11]) : 1}, ), "additional_info": ( "This is a test notice." if soln_status_bits[0] == 1 else None ), From 605de651c9a10958dc2c5cc1f2f6455f23571b2c Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Tue, 13 Aug 2024 13:32:21 -0400 Subject: [PATCH 09/12] changed additional_info in GECAM_GND --- gcn_classic_to_json/notices/GECAM_GND/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gcn_classic_to_json/notices/GECAM_GND/__init__.py b/gcn_classic_to_json/notices/GECAM_GND/__init__.py index 3868cdb..8b2b0a4 100644 --- a/gcn_classic_to_json/notices/GECAM_GND/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_GND/__init__.py @@ -13,6 +13,9 @@ def parse(bin): bin[20:39] # Unused. According to docs: '76 bytes for the future' soln_status_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) + comments = None + if soln_status_bits[0] == 1: + comments = "This is a test notice." return { "mission": "GECAM", @@ -34,7 +37,5 @@ def parse(bin): "latitude": bin[16] * 1e-2, "longitude": bin[17] * 1e-2, "classification": ({src_class_vals[bin[11] - 1]: 1},), - "additional_info": ( - "This is a test notice." if soln_status_bits[0] == 1 else None - ), + "additional_info": comments, } From 737792e62554759478b32c3bae3060daf0929d95 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Fri, 16 Aug 2024 11:45:59 -0400 Subject: [PATCH 10/12] Add trigger_duration --- gcn_classic_to_json/notices/GECAM_FLT/__init__.py | 1 + gcn_classic_to_json/notices/GECAM_FLT/example.json | 1 + 2 files changed, 2 insertions(+) diff --git a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py index 7f57834..252ff9e 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py @@ -17,6 +17,7 @@ def parse(bin): return { **parse_gecam_gnd(bin), "rate_snr": bin[20] * 1e-2, + "trigger_duration": bin[21] * 1e-4, "rate_energy_range": [bin[24], bin[25]], "detector_status": detectors, } diff --git a/gcn_classic_to_json/notices/GECAM_FLT/example.json b/gcn_classic_to_json/notices/GECAM_FLT/example.json index 3eb1569..fbcfe77 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/example.json +++ b/gcn_classic_to_json/notices/GECAM_FLT/example.json @@ -24,6 +24,7 @@ ], "additional_info": null, "rate_snr": 5.09, + "trigger_duration": 2.0, "rate_energy_range": [ 30, 140 From 873dcff36d0cbb5e38755b57daa1c6c93bf65a8b Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Wed, 28 Aug 2024 19:00:25 -0500 Subject: [PATCH 11/12] refactored GECAM_GND notices; added alert_tense; removed additional_info --- .../notices/GECAM_FLT/__init__.py | 2 +- .../notices/GECAM_FLT/example.json | 2 +- .../notices/GECAM_GND/__init__.py | 18 +++++++++--------- .../notices/GECAM_GND/example.json | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py index 252ff9e..971a684 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py @@ -1,6 +1,6 @@ import numpy as np -from ..GECAM_GND import parse as parse_gecam_gnd +from ..GECAM_GND import parse_gecam_gnd def parse(bin): diff --git a/gcn_classic_to_json/notices/GECAM_FLT/example.json b/gcn_classic_to_json/notices/GECAM_FLT/example.json index fbcfe77..5ff1c05 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/example.json +++ b/gcn_classic_to_json/notices/GECAM_FLT/example.json @@ -3,6 +3,7 @@ "id": [ 368 ], + "alert_tense": "current", "messenger": "EM", "mission_type": "B", "trigger_time": "2024-05-17T13:01:22.550Z", @@ -22,7 +23,6 @@ "SFLARE": 1 } ], - "additional_info": null, "rate_snr": 5.09, "trigger_duration": 2.0, "rate_energy_range": [ diff --git a/gcn_classic_to_json/notices/GECAM_GND/__init__.py b/gcn_classic_to_json/notices/GECAM_GND/__init__.py index 8b2b0a4..2230833 100644 --- a/gcn_classic_to_json/notices/GECAM_GND/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_GND/__init__.py @@ -8,18 +8,12 @@ containment_prob = norm().cdf(1) - norm.cdf(-1) -def parse(bin): - bin[15] # Unused. According to docs: '4 bytes for the future' - bin[20:39] # Unused. According to docs: '76 bytes for the future' - +def parse_gecam_gnd(bin): soln_status_bits = np.flip(np.unpackbits(bin[18:19].view(dtype="u1"))) - comments = None - if soln_status_bits[0] == 1: - comments = "This is a test notice." - return { "mission": "GECAM", "id": [bin[4]], + "alert_tense": "test" if soln_status_bits[0] == 1 else "current", "messenger": "EM", "mission_type": chr(bin[19] + 64), # There is an error here; You are supposed to divide by 8640000 to get the correct value @@ -37,5 +31,11 @@ def parse(bin): "latitude": bin[16] * 1e-2, "longitude": bin[17] * 1e-2, "classification": ({src_class_vals[bin[11] - 1]: 1},), - "additional_info": comments, } + + +def parse(bin): + bin[15] # Unused. According to docs: '4 bytes for the future' + bin[20:39] # Unused. According to docs: '76 bytes for the future' + + return {**parse_gecam_gnd(bin)} diff --git a/gcn_classic_to_json/notices/GECAM_GND/example.json b/gcn_classic_to_json/notices/GECAM_GND/example.json index 6d4eab4..461b744 100644 --- a/gcn_classic_to_json/notices/GECAM_GND/example.json +++ b/gcn_classic_to_json/notices/GECAM_GND/example.json @@ -3,6 +3,7 @@ "id": [ 368 ], + "alert_tense": "current", "messenger": "EM", "mission_type": "B", "trigger_time": "2024-05-17T13:01:22.550Z", @@ -21,6 +22,5 @@ { "SFLARE": 1 } - ], - "additional_info": null + ] } From f019a07c005e3c94445f7bc8f053001e3ea46ed8 Mon Sep 17 00:00:00 2001 From: Athish Thiruvengadam Date: Wed, 4 Sep 2024 14:40:52 -0500 Subject: [PATCH 12/12] Changed detector_status --- .../notices/GECAM_FLT/__init__.py | 2 +- .../notices/GECAM_FLT/example.json | 50 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py index 971a684..c509aaa 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/__init__.py +++ b/gcn_classic_to_json/notices/GECAM_FLT/__init__.py @@ -12,7 +12,7 @@ def parse(bin): detector_options = ["on", "triggered"] detector_bits = np.flip(np.unpackbits(bin[26:27].view(dtype="u1"))) detector_status = [detector_options[bit] for bit in detector_bits[:25]] - detectors = dict(zip(list(np.arange(25.0) + 1), detector_status)) + detectors = dict(zip(range(1, 26), detector_status)) return { **parse_gecam_gnd(bin), diff --git a/gcn_classic_to_json/notices/GECAM_FLT/example.json b/gcn_classic_to_json/notices/GECAM_FLT/example.json index 5ff1c05..a22e124 100644 --- a/gcn_classic_to_json/notices/GECAM_FLT/example.json +++ b/gcn_classic_to_json/notices/GECAM_FLT/example.json @@ -30,30 +30,30 @@ 140 ], "detector_status": { - "1.0": "triggered", - "2.0": "on", - "3.0": "on", - "4.0": "on", - "5.0": "on", - "6.0": "on", - "7.0": "on", - "8.0": "on", - "9.0": "on", - "10.0": "on", - "11.0": "triggered", - "12.0": "on", - "13.0": "on", - "14.0": "on", - "15.0": "on", - "16.0": "on", - "17.0": "triggered", - "18.0": "on", - "19.0": "on", - "20.0": "on", - "21.0": "on", - "22.0": "on", - "23.0": "on", - "24.0": "on", - "25.0": "on" + "1": "triggered", + "2": "on", + "3": "on", + "4": "on", + "5": "on", + "6": "on", + "7": "on", + "8": "on", + "9": "on", + "10": "on", + "11": "triggered", + "12": "on", + "13": "on", + "14": "on", + "15": "on", + "16": "on", + "17": "triggered", + "18": "on", + "19": "on", + "20": "on", + "21": "on", + "22": "on", + "23": "on", + "24": "on", + "25": "on" } }