Skip to content

Commit 67a794e

Browse files
authored
Use different alg for two_byte_sum that fixes off-by-one error (#315)
* Use different alg for two_byte_sum that fixes off-by-one error * ruff format 0.9 * Make fix that works for masked arrays * Add a unit test * Update test
1 parent 5e8dc51 commit 67a794e

File tree

10 files changed

+55
-27
lines changed

10 files changed

+55
-27
lines changed

mica/archive/aca_dark/dark_cal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def _get_dark_cal_id_scalar(date, select="before", dark_cal_ids=None):
141141
if ii < 0:
142142
earliest = CxoTime(dark_cal_secs[0]).date[:8]
143143
raise MissingDataError(
144-
f"No dark cal found before {earliest}" f"(requested dark cal on {date})"
144+
f"No dark cal found before {earliest}(requested dark cal on {date})"
145145
)
146146

147147
try:

mica/archive/aca_hdr3.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,21 @@
2323

2424

2525
def two_byte_sum(byte_msids, scale=1):
26-
def func(slot_data):
27-
return (
28-
(slot_data[byte_msids[0]].astype("int") >> 7) * (-1 * 65535)
29-
+ (slot_data[byte_msids[0]].astype("int") << 8)
30-
+ (slot_data[byte_msids[1]].astype("int"))
31-
) * scale
26+
def func(slot_data) -> np.ma.MaskedArray:
27+
# For each pair bytes0[i], bytes1[i], return the 16-bit signed integer
28+
# corresponding to those two bytes. The input bytes are unsigned.
29+
bytes0 = slot_data[byte_msids[0]].astype(np.uint8)
30+
bytes1 = slot_data[byte_msids[1]].astype(np.uint8)
31+
32+
# Make a 2xN array, then transpose to Nx2, then flatten to 2N, then copy to
33+
# get values continous in memory.
34+
bytes8_2xN = np.ma.vstack([bytes0, bytes1], dtype=np.uint8)
35+
bytes8 = bytes8_2xN.transpose().flatten().copy()
36+
37+
# Now view the 2N bytes as N 16-bit signed integers.
38+
ints16 = np.ma.array(bytes8.data.view(">i2"), mask=bytes8.mask[::2])
39+
40+
return ints16 * scale
3241

3342
return func
3443

mica/archive/cda/services.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,7 @@ def _get_cda_service_text(service, timeout=60, **params):
430430

431431
if not resp.ok:
432432
raise RuntimeError(
433-
f"got error {resp.status_code} for {resp.url}\n"
434-
f"{html_to_text(resp.text)}"
433+
f"got error {resp.status_code} for {resp.url}\n{html_to_text(resp.text)}"
435434
)
436435

437436
return resp.text
@@ -630,7 +629,7 @@ def get_ocat_local(
630629
# accurate enough for this application.
631630
where = (
632631
f"arccos(sin({ra * d2r})*sin(ra*{d2r}) + "
633-
f"cos({ra * d2r})*cos(ra*{d2r})*cos({dec*d2r}-dec*{d2r}))"
632+
f"cos({ra * d2r})*cos(ra*{d2r})*cos({dec * d2r}-dec*{d2r}))"
634633
f"< {radius / 60 * d2r}"
635634
)
636635
where_parts.append(where)

mica/archive/tests/test_aca_hdr3.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import numpy as np
99
import pytest
10+
from astropy.table import Table
1011

1112
from mica.archive import aca_hdr3
1213

@@ -63,3 +64,24 @@ def test_MSIDset():
6364
10679,
6465
]
6566
)
67+
68+
69+
def test_two_byte_sum():
70+
bytes0 = np.ma.array([0x00, 0xF0, 0x0F, 0xFF, 0xFF], dtype=np.uint8)
71+
bytes1 = np.ma.array([0x00, 0x0F, 0xF0, 0xFF, 0xFF], dtype=np.uint8)
72+
bytes0[-1] = np.ma.masked
73+
bytes1[-1] = np.ma.masked
74+
75+
# Original code prior to PR #315
76+
out1 = (
77+
(bytes0.astype("int") >> 7) * (-1 * 65535)
78+
+ (bytes0.astype("int") << 8)
79+
+ (bytes1.astype("int"))
80+
)
81+
assert np.all(out1 == np.ma.array([0, -4080, 4080, 0, 0], mask=[0, 0, 0, 0, 1]))
82+
83+
# New code in PR #315
84+
slot_data = Table([bytes0, bytes1], names=["byte0", "byte1"])
85+
ints16 = aca_hdr3.two_byte_sum(["byte0", "byte1"])(slot_data)
86+
87+
assert np.all(ints16 == np.ma.array([0, -4081, 4080, -1, 0], mask=[0, 0, 0, 0, 1]))

mica/centroid_dashboard.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,7 @@ def make_html(row_obsid, rows_slot, obs_dir):
11821182

11831183
for row in t_slot:
11841184
string += f"""<tr>
1185-
<td align='right'>{row['slot']}</td>
1185+
<td align='right'>{row["slot"]}</td>
11861186
"""
11871187
if row["id"] < 100:
11881188
id_ = ""
@@ -1193,13 +1193,13 @@ def make_html(row_obsid, rows_slot, obs_dir):
11931193
)
11941194

11951195
string += f"""<td align='right'>{id_}</td>
1196-
<td align='right'>{row['type']}</td>
1197-
<td align='right'>{row['mag']}</td>
1198-
<td align='right'>{row['yang']}</td>
1199-
<td align='right'>{row['zang']}</td>
1200-
<td align='right'>{row['median_mag']:.3f}</td>
1201-
<td align='right'>{row['median_dy']:.2f}</td>
1202-
<td align='right'>{row['median_dz']:.2f}</td>
1196+
<td align='right'>{row["type"]}</td>
1197+
<td align='right'>{row["mag"]}</td>
1198+
<td align='right'>{row["yang"]}</td>
1199+
<td align='right'>{row["zang"]}</td>
1200+
<td align='right'>{row["median_mag"]:.3f}</td>
1201+
<td align='right'>{row["median_dy"]:.2f}</td>
1202+
<td align='right'>{row["median_dz"]:.2f}</td>
12031203
</tr>
12041204
"""
12051205
string += f"""</table>

mica/report/report.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -920,9 +920,9 @@ def save_state_in_db(obsid, notes):
920920
del notes["last_sched"]
921921

922922
idcheck = db.fetchone(
923-
"select * from report_proc "
924-
"where obsid = '{}' "
925-
"and report_version = '{}'".format(obsid, REPORT_VERSION)
923+
"select * from report_proc where obsid = '{}' and report_version = '{}'".format(
924+
obsid, REPORT_VERSION
925+
)
926926
)
927927

928928
if idcheck is None:

mica/stats/update_acq_stats.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,7 @@ def get_options():
126126
parser.add_argument(
127127
"--email",
128128
action="append",
129-
help="email warning recipient, specify multiple times "
130-
"for multiple recipients",
129+
help="email warning recipient, specify multiple times for multiple recipients",
131130
)
132131
opt = parser.parse_args()
133132
return opt

mica/vv/process.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def update(obsids=None):
158158
logger.warn(f"Skipping obs:ver {obsid}:{obs['revision']}. Missing data")
159159
continue
160160
update_str = f"""UPDATE aspect_1_proc set vv_complete = {VV_VERSION}
161-
where obsid = {obsid} and revision = {obs['revision']}"""
161+
where obsid = {obsid} and revision = {obs["revision"]}"""
162162

163163
logger.info(update_str)
164164
with ska_dbi.DBI(dbi="sqlite", server=FILES["asp1_proc_table"]) as db:

mica/web/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def get_context_data(self, **kwargs):
8383
context["gui_table"] = gui_table
8484
reports_url = (
8585
"https://cxc.cfa.harvard.edu/mta/ASPECT/agasc/supplement_reports/stars/"
86-
f"{int(agasc_id//1e7):03d}/{agasc_id}/index.html"
86+
f"{int(agasc_id // 1e7):03d}/{agasc_id}/index.html"
8787
)
8888
context["reports_url"] = reports_url
8989
return context

scripts/update_agasc_supplement.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ def get_options(args=None):
3838
"--bad-star-source",
3939
type=int,
4040
help=(
41-
"Source identifier indicating provenance (default=max "
42-
"existing source + 1)"
41+
"Source identifier indicating provenance (default=max existing source + 1)"
4342
),
4443
)
4544
parser.add_argument(

0 commit comments

Comments
 (0)