From 8b6d7cc1b176189a7ec873df320f36b763f13914 Mon Sep 17 00:00:00 2001 From: Pey Lian Lim <2090236+pllim@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:27:32 -0500 Subject: [PATCH 1/6] Implement crosstalk correction for WFC3/UVIS following ACS/WFC style but adjusted for WFC3 pipeline flow. --- pkg/wfc3/Dates | 3 + pkg/wfc3/History | 3 + pkg/wfc3/Updates | 3 + pkg/wfc3/include/doccd.h | 1 + pkg/wfc3/include/wf3version.h | 4 +- pkg/wfc3/lib/CMakeLists.txt | 1 + pkg/wfc3/lib/wf3ccd/crosstalk.c | 132 ++++++++++++++++++++++++++++++++ pkg/wfc3/lib/wf3ccd/doccd.c | 11 ++- 8 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 pkg/wfc3/lib/wf3ccd/crosstalk.c diff --git a/pkg/wfc3/Dates b/pkg/wfc3/Dates index de3acdf9e..460a8bceb 100644 --- a/pkg/wfc3/Dates +++ b/pkg/wfc3/Dates @@ -1,3 +1,6 @@ +07-Feb-2026 - PLL - Version 3.8.0 + - New crosstalk correction for WFC3/UVIS fullframe unbinned exposures. + 07-Jan-2026 - PLL - Version 3.7.3 - Bug fix to remove per-second in BUNIT for CRREJ output for IR data. diff --git a/pkg/wfc3/History b/pkg/wfc3/History index 751e0b30b..9c097a298 100644 --- a/pkg/wfc3/History +++ b/pkg/wfc3/History @@ -1,3 +1,6 @@ +### 07-Feb-2026 - PLL - Version 3.8.0 + - New crosstalk correction for WFC3/UVIS fullframe unbinned exposures. + ### 07-Jan-2026 - PLL - Version 3.7.3 - Bug fix to remove per-second in BUNIT for CRREJ output for IR data. diff --git a/pkg/wfc3/Updates b/pkg/wfc3/Updates index 2ef336487..e6475a71d 100644 --- a/pkg/wfc3/Updates +++ b/pkg/wfc3/Updates @@ -1,3 +1,6 @@ +Updates for Version 3.8.0 07-Feb-2026 - PLL + - New crosstalk correction for WFC3/UVIS fullframe unbinned exposures. + Updates for Version 3.7.3 07-Jan-2026 - PLL - Bug fix to remove per-second in BUNIT for CRREJ output for IR data. diff --git a/pkg/wfc3/include/doccd.h b/pkg/wfc3/include/doccd.h index 5b3c0dd3f..e70e632cd 100644 --- a/pkg/wfc3/include/doccd.h +++ b/pkg/wfc3/include/doccd.h @@ -23,6 +23,7 @@ int doFlash (WF3Info *, SingleGroup *, float *); int flashHistory (WF3Info *, Hdr *); int doBlev (WF3Info *, SingleGroup *, int, float *, int *, int *); int blevHistory (WF3Info *, Hdr *, int, int); +void cross_talk_corr(WF3Info *, SingleGroup *); int CCDHistory (WF3Info *, Hdr *); int doDQI (WF3Info *, SingleGroup *, int overscan); int dqiHistory (WF3Info *, Hdr *); diff --git a/pkg/wfc3/include/wf3version.h b/pkg/wfc3/include/wf3version.h index 3a7c5e050..e0248af95 100644 --- a/pkg/wfc3/include/wf3version.h +++ b/pkg/wfc3/include/wf3version.h @@ -4,7 +4,7 @@ /* This string is written to the output primary header as CAL_VER. */ -# define WF3_CAL_VER "3.7.3 (Jan-07-2026)" -# define WF3_CAL_VER_NUM "3.7.3" +# define WF3_CAL_VER "3.8.0 (Feb-07-2026)" +# define WF3_CAL_VER_NUM "3.8.0" #endif /* INCL_WF3VERSION_H */ diff --git a/pkg/wfc3/lib/CMakeLists.txt b/pkg/wfc3/lib/CMakeLists.txt index a59725724..c7c288190 100644 --- a/pkg/wfc3/lib/CMakeLists.txt +++ b/pkg/wfc3/lib/CMakeLists.txt @@ -85,6 +85,7 @@ add_library(${PROJECT_NAME} SHARED wf32d/wf32d.c wf3ccd/blevdrift.c wf3ccd/blevfit.c + wf3ccd/crosstalk.c wf3ccd/doatod.c wf3ccd/dobias.c wf3ccd/doblev.c diff --git a/pkg/wfc3/lib/wf3ccd/crosstalk.c b/pkg/wfc3/lib/wf3ccd/crosstalk.c new file mode 100644 index 000000000..24806b92a --- /dev/null +++ b/pkg/wfc3/lib/wf3ccd/crosstalk.c @@ -0,0 +1,132 @@ +# include /* for sqrt */ + +#include "hstio.h" +#include "wf3.h" +#include "wf3info.h" + +static void dn_to_e (SingleGroup *, int, int, int, float *); +static void e_to_dn (SingleGroup *, int, int, int, float *); + + +/* remove amplifier cross-talk */ +void cross_talk_corr(WF3Info *wf3, SingleGroup *x) { + double temp; + const int arr_rows = x->sci.data.ny; + const int arr_cols = x->sci.data.nx; + int i, j; /* iteration variables */ + + /* cross talk scaling constant */ + double cross_scale = 9.1e-5; + + /* Convert to electrons */ + for (i = 0; i < arr_rows; i++) { + dn_to_e(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); + } + + for (i = 0; i < arr_rows; i++) { + for (j = 0; j < arr_cols; j++) { + temp = Pix(x->sci.data, arr_cols-j-1, i) * cross_scale; + Pix(x->sci.data, j, i) += (float) temp; + + /* Propagate error */ + Pix(x->err.data, j, i) = (float) sqrt(Pix(x->sci.data, j, i)); + } + } + + /* Convert back to DN */ + for (i = 0; i < arr_rows; i++) { + e_to_dn(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); + } +} + + +static void dn_to_e (SingleGroup *a, int line, int ampx, int ampy, float *gain) { + +/* arguments: +SingleGroupLine *a io: input data; output product +int line i: line number of input line +int ampx, ampy i: amp parameters +float *gain i: atogain parameter + +Adapted from multgn1d for fullframe only. +*/ + int i; + int dimx=a->sci.data.nx; + + /* Since both the science and error arrays operate on the + same line of data, we will combine them into one loop over + X values. + */ + if (line < ampy) { + /* This line has 2-AMP readout */ + /* Apply gain for first amp to first part of line */ + for (i = 0; i < ampx; i++) { + Pix(a->sci.data, i, line) = gain[AMP_C] * Pix(a->sci.data, i, line); + Pix(a->err.data, i, line) = gain[AMP_C] * Pix(a->err.data, i, line); + } + /* Apply gain for second amp over remainder of line */ + for (i = ampx; i < dimx; i++) { + Pix(a->sci.data, i, line) = gain[AMP_D] * Pix(a->sci.data, i, line); + Pix(a->err.data, i, line) = gain[AMP_D] * Pix(a->err.data, i, line); + } + + } else { + /* This line has 2-AMP readout */ + /* Apply gain for first amp to first part of line */ + for (i = 0; i < ampx; i++) { + Pix(a->sci.data, i, line) = gain[AMP_A] * Pix(a->sci.data, i, line); + Pix(a->err.data, i, line) = gain[AMP_A] * Pix(a->err.data, i, line); + } + /* Apply gain for second amp over remainder of line */ + for (i = ampx; i < dimx; i++) { + Pix(a->sci.data, i, line) = gain[AMP_B] * Pix(a->sci.data, i, line); + Pix(a->err.data, i, line) = gain[AMP_B] * Pix(a->err.data, i, line); + } + } +} + + +static void e_to_dn (SingleGroup *a, int line, int ampx, int ampy, float *gain) { + + /* arguments: + SingleGroupLine *a io: input data; output product + int line i: line number of input line + int ampx, ampy i: amp parameters + float *gain i: atogain parameter + + Adapted from multgn1d for fullframe only. + */ + int i; + int dimx=a->sci.data.nx; + + /* Since both the science and error arrays operate on the + same line of data, we will combine them into one loop over + X values. + */ + if (line < ampy) { + /* This line has 2-AMP readout */ + /* Apply gain for first amp to first part of line */ + for (i = 0; i < ampx; i++) { + Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_C]; + Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_C]; + } + /* Apply gain for second amp over remainder of line */ + for (i = ampx; i < dimx; i++) { + Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_D]; + Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_D]; + } + + } else { + /* This line has 2-AMP readout */ + /* Apply gain for first amp to first part of line */ + for (i = 0; i < ampx; i++) { + Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_A]; + Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_A]; + } + /* Apply gain for second amp over remainder of line */ + for (i = ampx; i < dimx; i++) { + Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_B]; + Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_B]; + } + } +} diff --git a/pkg/wfc3/lib/wf3ccd/doccd.c b/pkg/wfc3/lib/wf3ccd/doccd.c index de336435a..330d7efe2 100644 --- a/pkg/wfc3/lib/wf3ccd/doccd.c +++ b/pkg/wfc3/lib/wf3ccd/doccd.c @@ -101,6 +101,7 @@ int DoCCD (WF3Info *wf3, int extver) { int sizex, sizey; /* size of output image */ int i, j, x1, dx; /* loop index */ + int is_binned; int overscan; int blevcorr; char buff[SZ_FITS_REC+1]; @@ -319,6 +320,14 @@ int DoCCD (WF3Info *wf3, int extver) { if (biasHistory (wf3, x.globalhdr)) return (status); + is_binned = checkBinned(&x); + + /* Apply crosstalk correction after BIASCORR but before everything else. */ + if (wf3->biascorr == PERFORM && wf3->blevcorr == PERFORM && !is_binned & !wf3->subarray) { + trlmessage("\nCrosstalk correction is being performed."); + cross_talk_corr(wf3, &x); + } + /* Apply the saturation image. Strictly speaking, the application of the full-well saturation image is not a calibration step (i.e., there is no SATCORR), but the application @@ -346,7 +355,7 @@ int DoCCD (WF3Info *wf3, int extver) { BINNED IMAGES ARE NOT SUPPORTED AT ALL */ if (wf3->dqicorr == PERFORM) { - if (checkBinned(&x)){ + if (is_binned){ trlmessage("Binned data not supported for Sink Pixel detection"); } else { if (wf3->subarray){ From 0752fa81bbad7b382e383f14c612fd324cb6d598 Mon Sep 17 00:00:00 2001 From: Pey Lian Lim <2090236+pllim@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:39:27 -0500 Subject: [PATCH 2/6] Implement WFC3 ISR 2012-02 coefficients --- pkg/wfc3/lib/wf3ccd/crosstalk.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/pkg/wfc3/lib/wf3ccd/crosstalk.c b/pkg/wfc3/lib/wf3ccd/crosstalk.c index 24806b92a..92cf3ab1d 100644 --- a/pkg/wfc3/lib/wf3ccd/crosstalk.c +++ b/pkg/wfc3/lib/wf3ccd/crosstalk.c @@ -10,25 +10,41 @@ static void e_to_dn (SingleGroup *, int, int, int, float *); /* remove amplifier cross-talk */ void cross_talk_corr(WF3Info *wf3, SingleGroup *x) { - double temp; const int arr_rows = x->sci.data.ny; const int arr_cols = x->sci.data.nx; int i, j; /* iteration variables */ - /* cross talk scaling constant */ - double cross_scale = 9.1e-5; - /* Convert to electrons */ for (i = 0; i < arr_rows; i++) { dn_to_e(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); } + /* WFC3 ISR 2012-02 */ for (i = 0; i < arr_rows; i++) { - for (j = 0; j < arr_cols; j++) { - temp = Pix(x->sci.data, arr_cols-j-1, i) * cross_scale; - Pix(x->sci.data, j, i) += (float) temp; + if (i < wf3->ampy) { + for (j = 0; j < wf3->ampx; j++) { + // c_corr = c - (-0.038376406 + np.flip(d, 1) * -0.079701178) + Pix(x->sci.data, j, i) -= -0.038376406 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.079701178; + } + for (j = wf3->ampx; j < arr_cols; j++) { + // d_corr = d - (0.19124641 + np.flip(c, 1) * -0.23177171) + Pix(x->sci.data, j, i) -= 0.19124641 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.23177171; + } + } else { + for (j = 0; j < wf3->ampx; j++) { + // a_corr = a - (0.0180206 + np.flip(b, 1) * -0.060494304) + Pix(x->sci.data, j, i) -= 0.0180206 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.060494304; + } + for (j = wf3->ampx; j < arr_cols; j++) { + // b_corr = b - (0.15501201 + np.flip(a, 1) * -0.20746221) + Pix(x->sci.data, j, i) -= 0.15501201 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.20746221; + } + } + } - /* Propagate error */ + /* Propagate error */ + for (i = 0; i < arr_rows; i++) { + for (j = 0; j < arr_cols; j++) { Pix(x->err.data, j, i) = (float) sqrt(Pix(x->sci.data, j, i)); } } From 5e58f1656492fd7f925933c34145371a22fbd5af Mon Sep 17 00:00:00 2001 From: Pey Lian Lim <2090236+pllim@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:54:21 -0500 Subject: [PATCH 3/6] Fix sqrt calc for ERR --- pkg/wfc3/lib/wf3ccd/crosstalk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/wfc3/lib/wf3ccd/crosstalk.c b/pkg/wfc3/lib/wf3ccd/crosstalk.c index 92cf3ab1d..9c45a0c63 100644 --- a/pkg/wfc3/lib/wf3ccd/crosstalk.c +++ b/pkg/wfc3/lib/wf3ccd/crosstalk.c @@ -45,7 +45,7 @@ void cross_talk_corr(WF3Info *wf3, SingleGroup *x) { /* Propagate error */ for (i = 0; i < arr_rows; i++) { for (j = 0; j < arr_cols; j++) { - Pix(x->err.data, j, i) = (float) sqrt(Pix(x->sci.data, j, i)); + Pix(x->err.data, j, i) = (float) sqrt(fabs(Pix(x->sci.data, j, i))); } } From f5afd9c7555e284bf165707750d742676da6611b Mon Sep 17 00:00:00 2001 From: Pey Lian Lim <2090236+pllim@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:25:32 -0500 Subject: [PATCH 4/6] Clean up logic, fix ERR propagation --- pkg/wfc3/include/doccd.h | 2 +- pkg/wfc3/lib/wf3ccd/crosstalk.c | 61 +++++++++++++++++++-------------- pkg/wfc3/lib/wf3ccd/doccd.c | 4 ++- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/pkg/wfc3/include/doccd.h b/pkg/wfc3/include/doccd.h index e70e632cd..4e04a0d2d 100644 --- a/pkg/wfc3/include/doccd.h +++ b/pkg/wfc3/include/doccd.h @@ -23,7 +23,7 @@ int doFlash (WF3Info *, SingleGroup *, float *); int flashHistory (WF3Info *, Hdr *); int doBlev (WF3Info *, SingleGroup *, int, float *, int *, int *); int blevHistory (WF3Info *, Hdr *, int, int); -void cross_talk_corr(WF3Info *, SingleGroup *); +int cross_talk_corr(WF3Info *, SingleGroup *); int CCDHistory (WF3Info *, Hdr *); int doDQI (WF3Info *, SingleGroup *, int overscan); int dqiHistory (WF3Info *, Hdr *); diff --git a/pkg/wfc3/lib/wf3ccd/crosstalk.c b/pkg/wfc3/lib/wf3ccd/crosstalk.c index 9c45a0c63..a2b622210 100644 --- a/pkg/wfc3/lib/wf3ccd/crosstalk.c +++ b/pkg/wfc3/lib/wf3ccd/crosstalk.c @@ -9,43 +9,52 @@ static void e_to_dn (SingleGroup *, int, int, int, float *); /* remove amplifier cross-talk */ -void cross_talk_corr(WF3Info *wf3, SingleGroup *x) { +int cross_talk_corr(WF3Info *wf3, SingleGroup *x) { + extern int status; const int arr_rows = x->sci.data.ny; const int arr_cols = x->sci.data.nx; - int i, j; /* iteration variables */ + int i, j, cur_amp; /* iteration variables */ + float corr_fac, cur_err, *y; + + /* Correction coefficients (ABCD) from WFC3 ISR 2012-02 */ + const float intercept[NAMPS] = {0.0180206, 0.15501201,-0.038376406, 0.19124641}; + const float slope[NAMPS] = {-0.060494304, -0.20746221, -0.079701178, -0.23177171}; /* Convert to electrons */ for (i = 0; i < arr_rows; i++) { dn_to_e(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); } - /* WFC3 ISR 2012-02 */ + /* Crosstalk correction */ for (i = 0; i < arr_rows; i++) { - if (i < wf3->ampy) { - for (j = 0; j < wf3->ampx; j++) { - // c_corr = c - (-0.038376406 + np.flip(d, 1) * -0.079701178) - Pix(x->sci.data, j, i) -= -0.038376406 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.079701178; - } - for (j = wf3->ampx; j < arr_cols; j++) { - // d_corr = d - (0.19124641 + np.flip(c, 1) * -0.23177171) - Pix(x->sci.data, j, i) -= 0.19124641 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.23177171; - } - } else { - for (j = 0; j < wf3->ampx; j++) { - // a_corr = a - (0.0180206 + np.flip(b, 1) * -0.060494304) - Pix(x->sci.data, j, i) -= 0.0180206 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.060494304; - } - for (j = wf3->ampx; j < arr_cols; j++) { - // b_corr = b - (0.15501201 + np.flip(a, 1) * -0.20746221) - Pix(x->sci.data, j, i) -= 0.15501201 + Pix(x->sci.data, arr_cols - j - 1, i) * -0.20746221; - } + /* Copy out original row for corr_fac calculation. */ + if ((y = calloc(arr_cols, sizeof(float))) == NULL) { + return (status = OUT_OF_MEMORY); + } + for (j = 0; j < arr_cols; j++) { + y[j] = Pix(x->sci.data, j, i); } - } - /* Propagate error */ - for (i = 0; i < arr_rows; i++) { for (j = 0; j < arr_cols; j++) { - Pix(x->err.data, j, i) = (float) sqrt(fabs(Pix(x->sci.data, j, i))); + if (i < wf3->ampy) { + if (j < wf3->ampx) { + cur_amp = AMP_C; + } else { + cur_amp = AMP_D; + } + } else { + if (j < wf3->ampx) { + cur_amp = AMP_A; + } else { + cur_amp = AMP_B; + } + } + corr_fac = intercept[cur_amp] + y[arr_cols - j - 1] * slope[cur_amp]; + Pix(x->sci.data, j, i) = y[j] - corr_fac; + + /* Propagate error; assume ERR of correction is sqrt(corr_fac) */ + cur_err = Pix(x->err.data, j, i); + Pix(x->err.data, j, i) = (float) sqrt(cur_err * cur_err + fabs(corr_fac)); } } @@ -53,6 +62,8 @@ void cross_talk_corr(WF3Info *wf3, SingleGroup *x) { for (i = 0; i < arr_rows; i++) { e_to_dn(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); } + + return status; } diff --git a/pkg/wfc3/lib/wf3ccd/doccd.c b/pkg/wfc3/lib/wf3ccd/doccd.c index 330d7efe2..44eeade6e 100644 --- a/pkg/wfc3/lib/wf3ccd/doccd.c +++ b/pkg/wfc3/lib/wf3ccd/doccd.c @@ -325,7 +325,9 @@ int DoCCD (WF3Info *wf3, int extver) { /* Apply crosstalk correction after BIASCORR but before everything else. */ if (wf3->biascorr == PERFORM && wf3->blevcorr == PERFORM && !is_binned & !wf3->subarray) { trlmessage("\nCrosstalk correction is being performed."); - cross_talk_corr(wf3, &x); + if (cross_talk_corr(wf3, &x)) { + return (status); + } } /* Apply the saturation image. From 3926f98d4fda8540162715ff173d8a658ba6f1b7 Mon Sep 17 00:00:00 2001 From: Pey Lian Lim <2090236+pllim@users.noreply.github.com> Date: Wed, 11 Feb 2026 22:58:43 -0500 Subject: [PATCH 5/6] Bug fix rabbit hole --- pkg/wfc3/lib/wf3ccd/crosstalk.c | 124 +++----------------------------- 1 file changed, 10 insertions(+), 114 deletions(-) diff --git a/pkg/wfc3/lib/wf3ccd/crosstalk.c b/pkg/wfc3/lib/wf3ccd/crosstalk.c index a2b622210..51fe93bb1 100644 --- a/pkg/wfc3/lib/wf3ccd/crosstalk.c +++ b/pkg/wfc3/lib/wf3ccd/crosstalk.c @@ -4,9 +4,6 @@ #include "wf3.h" #include "wf3info.h" -static void dn_to_e (SingleGroup *, int, int, int, float *); -static void e_to_dn (SingleGroup *, int, int, int, float *); - /* remove amplifier cross-talk */ int cross_talk_corr(WF3Info *wf3, SingleGroup *x) { @@ -14,18 +11,14 @@ int cross_talk_corr(WF3Info *wf3, SingleGroup *x) { const int arr_rows = x->sci.data.ny; const int arr_cols = x->sci.data.nx; int i, j, cur_amp; /* iteration variables */ - float corr_fac, cur_err, *y; + float *y; + double corr_fac, cur_err; /* Correction coefficients (ABCD) from WFC3 ISR 2012-02 */ - const float intercept[NAMPS] = {0.0180206, 0.15501201,-0.038376406, 0.19124641}; - const float slope[NAMPS] = {-0.060494304, -0.20746221, -0.079701178, -0.23177171}; - - /* Convert to electrons */ - for (i = 0; i < arr_rows; i++) { - dn_to_e(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); - } + const double intercept[NAMPS] = {0.0180206, 0.15501201,-0.038376406, 0.19124641}; + const double slope[NAMPS] = {-6.0494304e-5, -2.0746221e-4, -7.9701178e-5, -2.3177171e-4}; - /* Crosstalk correction */ + /* Crosstalk correction in electrons but then converted back to DN */ for (i = 0; i < arr_rows; i++) { /* Copy out original row for corr_fac calculation. */ if ((y = calloc(arr_cols, sizeof(float))) == NULL) { @@ -36,7 +29,7 @@ int cross_talk_corr(WF3Info *wf3, SingleGroup *x) { } for (j = 0; j < arr_cols; j++) { - if (i < wf3->ampy) { + if (x->group_num == 1) { if (j < wf3->ampx) { cur_amp = AMP_C; } else { @@ -49,111 +42,14 @@ int cross_talk_corr(WF3Info *wf3, SingleGroup *x) { cur_amp = AMP_B; } } - corr_fac = intercept[cur_amp] + y[arr_cols - j - 1] * slope[cur_amp]; - Pix(x->sci.data, j, i) = y[j] - corr_fac; + corr_fac = intercept[cur_amp] + y[arr_cols - j - 1] * wf3->atodgain[cur_amp] * slope[cur_amp]; + Pix(x->sci.data, j, i) = (y[j] * wf3->atodgain[cur_amp] - corr_fac) / wf3->atodgain[cur_amp]; /* Propagate error; assume ERR of correction is sqrt(corr_fac) */ - cur_err = Pix(x->err.data, j, i); - Pix(x->err.data, j, i) = (float) sqrt(cur_err * cur_err + fabs(corr_fac)); + cur_err = Pix(x->err.data, j, i) * wf3->atodgain[cur_amp]; + Pix(x->err.data, j, i) = sqrt(cur_err * cur_err + fabs(corr_fac)) / wf3->atodgain[cur_amp]; } } - /* Convert back to DN */ - for (i = 0; i < arr_rows; i++) { - e_to_dn(x, i, wf3->ampx, wf3->ampy, wf3->atodgain); - } - return status; } - - -static void dn_to_e (SingleGroup *a, int line, int ampx, int ampy, float *gain) { - -/* arguments: -SingleGroupLine *a io: input data; output product -int line i: line number of input line -int ampx, ampy i: amp parameters -float *gain i: atogain parameter - -Adapted from multgn1d for fullframe only. -*/ - int i; - int dimx=a->sci.data.nx; - - /* Since both the science and error arrays operate on the - same line of data, we will combine them into one loop over - X values. - */ - if (line < ampy) { - /* This line has 2-AMP readout */ - /* Apply gain for first amp to first part of line */ - for (i = 0; i < ampx; i++) { - Pix(a->sci.data, i, line) = gain[AMP_C] * Pix(a->sci.data, i, line); - Pix(a->err.data, i, line) = gain[AMP_C] * Pix(a->err.data, i, line); - } - /* Apply gain for second amp over remainder of line */ - for (i = ampx; i < dimx; i++) { - Pix(a->sci.data, i, line) = gain[AMP_D] * Pix(a->sci.data, i, line); - Pix(a->err.data, i, line) = gain[AMP_D] * Pix(a->err.data, i, line); - } - - } else { - /* This line has 2-AMP readout */ - /* Apply gain for first amp to first part of line */ - for (i = 0; i < ampx; i++) { - Pix(a->sci.data, i, line) = gain[AMP_A] * Pix(a->sci.data, i, line); - Pix(a->err.data, i, line) = gain[AMP_A] * Pix(a->err.data, i, line); - } - /* Apply gain for second amp over remainder of line */ - for (i = ampx; i < dimx; i++) { - Pix(a->sci.data, i, line) = gain[AMP_B] * Pix(a->sci.data, i, line); - Pix(a->err.data, i, line) = gain[AMP_B] * Pix(a->err.data, i, line); - } - } -} - - -static void e_to_dn (SingleGroup *a, int line, int ampx, int ampy, float *gain) { - - /* arguments: - SingleGroupLine *a io: input data; output product - int line i: line number of input line - int ampx, ampy i: amp parameters - float *gain i: atogain parameter - - Adapted from multgn1d for fullframe only. - */ - int i; - int dimx=a->sci.data.nx; - - /* Since both the science and error arrays operate on the - same line of data, we will combine them into one loop over - X values. - */ - if (line < ampy) { - /* This line has 2-AMP readout */ - /* Apply gain for first amp to first part of line */ - for (i = 0; i < ampx; i++) { - Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_C]; - Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_C]; - } - /* Apply gain for second amp over remainder of line */ - for (i = ampx; i < dimx; i++) { - Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_D]; - Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_D]; - } - - } else { - /* This line has 2-AMP readout */ - /* Apply gain for first amp to first part of line */ - for (i = 0; i < ampx; i++) { - Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_A]; - Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_A]; - } - /* Apply gain for second amp over remainder of line */ - for (i = ampx; i < dimx; i++) { - Pix(a->sci.data, i, line) = Pix(a->sci.data, i, line) / gain[AMP_B]; - Pix(a->err.data, i, line) = Pix(a->err.data, i, line) / gain[AMP_B]; - } - } -} From 663a364a007b717d3a7b8232b91d9f7f6950cf3a Mon Sep 17 00:00:00 2001 From: Pey Lian Lim <2090236+pllim@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:22:26 -0500 Subject: [PATCH 6/6] Only add back signal --- pkg/wfc3/lib/wf3ccd/crosstalk.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/wfc3/lib/wf3ccd/crosstalk.c b/pkg/wfc3/lib/wf3ccd/crosstalk.c index 51fe93bb1..099fd0d9c 100644 --- a/pkg/wfc3/lib/wf3ccd/crosstalk.c +++ b/pkg/wfc3/lib/wf3ccd/crosstalk.c @@ -43,11 +43,14 @@ int cross_talk_corr(WF3Info *wf3, SingleGroup *x) { } } corr_fac = intercept[cur_amp] + y[arr_cols - j - 1] * wf3->atodgain[cur_amp] * slope[cur_amp]; - Pix(x->sci.data, j, i) = (y[j] * wf3->atodgain[cur_amp] - corr_fac) / wf3->atodgain[cur_amp]; + /* Only fix when we can recover the signal, not removing more signal */ + if (corr_fac < 0) { + Pix(x->sci.data, j, i) = (y[j] * wf3->atodgain[cur_amp] - corr_fac) / wf3->atodgain[cur_amp]; - /* Propagate error; assume ERR of correction is sqrt(corr_fac) */ - cur_err = Pix(x->err.data, j, i) * wf3->atodgain[cur_amp]; - Pix(x->err.data, j, i) = sqrt(cur_err * cur_err + fabs(corr_fac)) / wf3->atodgain[cur_amp]; + /* Propagate error; assume ERR of correction is sqrt(corr_fac) */ + cur_err = Pix(x->err.data, j, i) * wf3->atodgain[cur_amp]; + Pix(x->err.data, j, i) = sqrt(cur_err * cur_err + fabs(corr_fac)) / wf3->atodgain[cur_amp]; + } } }