bar_hits, DataEvent event) {
+ this.barHits = bar_hits;
+ this.computeClusterProperties();
+ }
+
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ }
+
+}
diff --git a/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/constants/Parameters.java b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/constants/Parameters.java
new file mode 100644
index 0000000000..a32164b9ec
--- /dev/null
+++ b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/constants/Parameters.java
@@ -0,0 +1,40 @@
+package org.jlab.rec.recoiltof.constants;
+
+/**
+ *
+ * @author npilleux, Nilanga Wickramaarachchi
+ */
+public class Parameters {
+
+ public static final int NROWS = 5; //number of rows of bars in a sector
+ public static final int NCOLUMNS = 63; //number of columns of bars in a sector
+
+ public static final double LONG_BAR_LENGTH = 275; // mm
+ public static final double SHORT_BAR_LENGTH = 40; // mm
+
+ public static final double BAR_WIDTH = 10; // mm
+
+ public static final double WIDTH = NCOLUMNS * BAR_WIDTH;
+ public static final double LENGTH = (NROWS-1) * LONG_BAR_LENGTH + SHORT_BAR_LENGTH;
+
+ public static final double HORIZONTAL_STARTING_ANGLE = 40.;
+ public static final double HORIZONTAL_OPENING_ANGLE = 29.;
+ public static final double RADIUS = 1220; // mm
+
+ public static final double VEFF = 200.0;//mm/ns
+ public static final double TDC2TIME = 0.015625;//ns per channel bin
+ public static final double ATT_L = 1600.0;//mm
+ public static final double TOT2ENERGY = 1.956 * 0.5 /1000;//to MeV
+
+ //public static double SIGMA_Y_TRACK_MATCHING_BAR = 200;//in mm
+ public static double SIGMA_Y_CLUSTERING = 40;//in mm
+ public static double SIGMA_T_CLUSTERING = 100;// in ns
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ // TODO code application logic here
+ }
+
+}
diff --git a/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/BarHit.java b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/BarHit.java
new file mode 100644
index 0000000000..9d1b3b106f
--- /dev/null
+++ b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/BarHit.java
@@ -0,0 +1,148 @@
+package org.jlab.rec.recoiltof.hit;
+
+import org.jlab.rec.recoiltof.constants.Parameters;
+
+/**
+ *
+ * Represents a hit in the recoil tof bar. Extends class RECOILTOFHit. Is further defined
+ * by the two hits upstream and downstream composing a full bar hit. y position,
+ * time and energy are defined from the up/down hits.
+ *
+ * @author npilleux, Nilanga Wickramaarachchi
+ */
+public class BarHit extends RECOILTOFHit {
+
+ //A bar hit is the combination of a downstream and upstream hits
+ private RECOILTOFHit hitUp, hitDown;
+
+ public RECOILTOFHit getHitUp() {
+ return hitUp;
+ }
+
+ public void setHitUp(RECOILTOFHit hit_up) {
+ this.hitUp = hit_up;
+ }
+
+ public RECOILTOFHit getHitDown() {
+ return hitDown;
+ }
+
+ public void setHitDown(RECOILTOFHit hit_down) {
+ this.hitDown = hit_down;
+ }
+
+ /**
+ * Computes bar hit y local coordinate from up/downstream hit times.
+ *
+ */
+ public final void computeLocalY() {
+ this.setLocalY(Parameters.VEFF/2. * (hitUp.getTime() - hitDown.getTime()));
+ }
+
+ /**
+ * Computes bar hit y coordinate in the global coordinate system.
+ *
+ */
+ public final void computeGlobalY() {
+ double localY = this.getLocalY();
+
+ int nRows = Parameters.NROWS;
+ double y_start = -(Parameters.LENGTH - Parameters.LONG_BAR_LENGTH)/2; // Starting Y position
+ double dy_long = Parameters.LONG_BAR_LENGTH;
+ double dy_short = Parameters.SHORT_BAR_LENGTH;
+
+ double y_pos; // y coordinate of the center of bar wrt to the global coordinate system
+ if(hitUp.getRow()-1 < (nRows - 1)/2)
+ {
+ y_pos = y_start + ((hitUp.getRow()-1) * dy_long);
+ }
+ else if (hitUp.getRow()-1 == (nRows-1) / 2) // middle row
+ {
+ y_pos = 0;
+ }
+ else
+ {
+ y_pos = y_start + (hitUp.getRow()-2) * dy_long + dy_short;
+ }
+
+ this.setY(y_pos + localY);
+ }
+
+
+ /**
+ * Computes bar hit time from up/downstream hit times.
+ * The time is set as the time of the most energetic hit.
+ * It is corrected for propagation time.
+ *
+ */
+ public final void computeTime() {
+ //We pick the most energetic signal as the timing signal
+ double time_at_sipm, distance_to_sipm;
+ if(this.hitDown.getEnergy() > this.hitUp.getEnergy()) {
+ time_at_sipm = this.hitDown.getTime();
+ if(this.hitDown.getRow() == 3) distance_to_sipm = Parameters.SHORT_BAR_LENGTH/2. - this.getLocalY();
+ else distance_to_sipm = Parameters.LONG_BAR_LENGTH/2. - this.getLocalY();
+ }
+ else {
+ time_at_sipm = this.hitUp.getTime();
+ if(this.hitUp.getRow() == 3) distance_to_sipm = Parameters.SHORT_BAR_LENGTH/2. + this.getLocalY();
+ else distance_to_sipm = Parameters.LONG_BAR_LENGTH/2. + this.getLocalY();
+ }
+ this.setTime(time_at_sipm - distance_to_sipm/Parameters.VEFF);
+ }
+
+ /**
+ * Computes bar hit energy from up/downstream hits.
+ * The energy of the up/downstream hits is corrected for attenuation now that y is known.
+ * The energy of the bar hit is the sum of the energy of the up/downstream hits.
+ *
+ */
+ public final void computeEnergy() {
+ this.computeLocalY();
+ double distance_hit_to_sipm_up, distance_hit_to_sipm_down;
+
+ if (hitUp.getRow() == 3) distance_hit_to_sipm_up = Parameters.SHORT_BAR_LENGTH / 2. + this.getLocalY();
+ else distance_hit_to_sipm_up = Parameters.LONG_BAR_LENGTH / 2. + this.getLocalY();
+
+ if (hitDown.getRow() == 3) distance_hit_to_sipm_down = Parameters.SHORT_BAR_LENGTH / 2. - this.getLocalY();
+ else distance_hit_to_sipm_down = Parameters.LONG_BAR_LENGTH / 2. - this.getLocalY();
+
+ double Edep_up = hitUp.getEnergy() * Math.exp(distance_hit_to_sipm_up / Parameters.ATT_L);
+ double Edep_down = hitDown.getEnergy() * Math.exp(distance_hit_to_sipm_down / Parameters.ATT_L);
+ this.setEnergy(Edep_up + Edep_down);
+ }
+
+ public BarHit(RECOILTOFHit hit_down, RECOILTOFHit hit_up) {
+ boolean hits_match = hit_down.matchBar(hit_up);
+ if (!hits_match) {
+ throw new UnsupportedOperationException("Hits do not match \n");
+ }
+ this.setType("bar");
+ this.setOrder(2);//Fake order for bar hits
+ this.hitUp = hit_up;
+ this.hitDown = hit_down;
+ this.setSector(hit_up.getSector());
+ this.setRow(hit_up.getRow());
+ this.setColumn(hit_up.getColumn());
+ this.setX(hit_up.getX());
+ this.setZ(hit_up.getZ());
+ this.computeLocalY();
+ this.computeGlobalY();
+ this.computeTime();
+ this.computeEnergy();
+ this.setTdc((hit_down.getTdc() + hit_up.getTdc())/2);
+ this.setTot((hit_down.getTot() + hit_up.getTot()));
+ }
+
+ public BarHit() {
+ super();
+ this.setType("bar");
+ this.setOrder(2);//Fake order for bar hits
+ }
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ }
+}
diff --git a/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/HitFinder.java b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/HitFinder.java
new file mode 100644
index 0000000000..25ac83a8ae
--- /dev/null
+++ b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/HitFinder.java
@@ -0,0 +1,119 @@
+package org.jlab.rec.recoiltof.hit;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import org.jlab.io.base.DataBank;
+import org.jlab.io.base.DataEvent;
+
+/**
+ * The {@code HitFinder} class finds hits in the recoil tof.
+ *
+ *
+ * Uses recoil tof tdc bank information
+ *
+ * Creates a {@link ArrayList} of {@link BarHit} for bar hits read.
+ *
+ *
+ *
+ * @author pilleux, Nilanga Wickramaarachchi
+ */
+public class HitFinder {
+
+ /**
+ * list of bar hits
+ */
+ private ArrayList barHits;
+
+ /**
+ * Default constructor that initializes the list of hits as new empty lists.
+ */
+ public HitFinder() {
+ this.barHits = new ArrayList<>();
+ }
+
+ // Getter and Setter for barHits
+ public ArrayList getBarHits() {
+ return barHits;
+ }
+
+ public void setBarHits(ArrayList bar_hits) {
+ this.barHits = bar_hits;
+ }
+
+ /**
+ *
+ * @param event the {@link DataEvent} containing hits.
+ *
+ */
+ public void findHits(DataEvent event) {
+ //For each event a list of bar hits is filled
+ this.barHits.clear();
+ //They are read from the RECOILTOF TDC bank
+ DataBank bank = event.getBank("RECOILTOF::tdc");
+ int nt = bank.rows(); // number of hits
+ //Hits in the bar downstream and upstream will be matched
+ ArrayList hit_up = new ArrayList<>();
+ ArrayList hit_down = new ArrayList<>();
+
+ //Looping through all hits
+ for (int i = 0; i < nt; i++) {
+ //Getting their properties
+ int sector = bank.getInt("sector", i);
+ int row = bank.getInt("row", i);
+ int column = bank.getInt("column", i);
+ int order = bank.getInt("order", i);
+ int tdc = bank.getInt("TDC", i);
+ int tot = bank.getInt("ToT", i);
+
+ //Building a Hit
+ RECOILTOFHit hit = new RECOILTOFHit(sector, row, column, order, tdc, tot);
+ if (hit.getEnergy() < 0.01) {
+ continue; //energy threshold
+ }
+
+ //Sorting the hits into upstream and downstream bar hits
+ //Lists are built for up/down bar to match them after
+ if (null == hit.getType()) {
+ System.out.print("Undefined hit type \n");
+ } else {
+ switch (hit.getType()) {
+ case "bar up" ->
+ hit_up.add(hit);
+ case "bar down" ->
+ hit_down.add(hit);
+ default ->
+ System.out.print("Undefined hit type \n");
+ }
+ }
+ }//End loop through all hits
+
+ //Starting loop through up hits in the bar
+ for (int i_up = 0; i_up < hit_up.size(); i_up++) {
+ RECOILTOFHit this_hit_up = hit_up.get(i_up);
+ int countMatches = 0;
+ //Starting loop through down hits in the bar
+ for (int i_down = 0; i_down < hit_down.size(); i_down++) {
+ RECOILTOFHit this_hit_down = hit_down.get(i_down);
+ //Matching the hits: if same bar and different order, they make up a bar hit
+ if (this_hit_up.matchBar(this_hit_down)) {
+ if (countMatches > 0) {
+ //If the up hit was already involved in a match, do not make an additionnal match
+ //Chosing to ignore double matches for now because it happened for <1% of events in cosmic runs
+ continue;
+ }
+ BarHit this_bar_hit = new BarHit(this_hit_down, this_hit_up);
+ this.barHits.add(this_bar_hit);
+ countMatches++;
+ }
+ }
+ }
+ //Once all has been listed, hits are sorted by energy
+ Collections.sort(this.barHits, (hit1, hit2) -> Double.compare(hit2.getEnergy(), hit1.getEnergy()));
+ }
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ }
+}
diff --git a/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/RECOILTOFHit.java b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/RECOILTOFHit.java
new file mode 100644
index 0000000000..c4a557d49e
--- /dev/null
+++ b/reconstruction/recoiltof/src/main/java/org/jlab/rec/recoiltof/hit/RECOILTOFHit.java
@@ -0,0 +1,406 @@
+package org.jlab.rec.recoiltof.hit;
+
+import org.jlab.geom.base.*;
+import org.jlab.geom.prim.Point3D;
+import org.jlab.rec.recoiltof.constants.Parameters;
+
+/**
+ *
+ * Represents a hit in the recoil tof. Stores info about the sector, row, column,
+ * order, TDC, ToT. Type is bar up/bar down/ bar. Stores whether
+ * the hit is part of a cluster. Calculates time, energy based on TDC/ToT.
+ *
+ * @author npilleux, Nilanga Wickramaarachchi
+ */
+public class RECOILTOFHit {
+
+ private int sector, row, column, order;
+ private int tdc, tot;
+ private double time, energy, x, y, z, local_y;
+ private String type;
+ private boolean isInACluster;
+ private int associatedClusterIndex;
+ int idTDC;
+
+ public int getSector() {
+ return sector;
+ }
+
+ public void setSector(int sector) {
+ this.sector = sector;
+ }
+
+ public int getRow() {
+ return row;
+ }
+
+ public void setRow(int row) {
+ this.row = row;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public void setColumn(int column) {
+ this.column = column;
+ }
+
+ public int getTdc() {
+ return tdc;
+ }
+
+ public void setTdc(int tdc) {
+ this.tdc = tdc;
+ }
+
+ public int getTot() {
+ return tot;
+ }
+
+ public void setTot(int tot) {
+ this.tot = tot;
+ }
+
+ public double getTime() {
+ return time;
+ }
+
+ public void setTime(double time) {
+ this.time = time;
+ }
+
+ public double getEnergy() {
+ return energy;
+ }
+
+ public void setEnergy(double energy) {
+ this.energy = energy;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ }
+
+ public double getLocalY() {
+ return local_y;
+ }
+
+ public void setLocalY(double local_y) {
+ this.local_y = local_y;
+ }
+
+ public double getZ() {
+ return z;
+ }
+
+ public void setZ(double z) {
+ this.z = z;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public boolean getIsInACluster() {
+ return isInACluster;
+ }
+
+ public void setIsInACluster(boolean is_in_a_cluster) {
+ this.isInACluster = is_in_a_cluster;
+ }
+
+ public int getAssociatedClusterIndex() {
+ return associatedClusterIndex;
+ }
+
+ public void setAssociatedClusterIndex(int index) {
+ this.associatedClusterIndex = index;
+ }
+
+ public int getIdTDC() {
+ return idTDC;
+ }
+
+ public void setIdTDC(int index) {
+ this.idTDC = index;
+ }
+
+
+ /**
+ * Assigns a type to the hit.
+ *
+ */
+ public final String makeType() {
+ //Type of hit can be bar up, bar down or bar.
+ //Avoids testing components and order every time.
+ String itype = "undefined";
+ if (this.order == 0) {
+ itype = "bar down";
+ } else if (this.order == 1) {
+ itype = "bar up";
+ }
+ this.type = itype;
+ return itype;
+ }
+
+ /**
+ * Converts TDC to time (ns). Sets the hit time parameter to a raw time for
+ * up/down bar hits.
+ *
+ * @return 0 if the time was successfully set, or 1 if the hit type is
+ * unsupported.
+ */
+ public final int convertTdcToTime() {
+ double tdc2time, veff, distance_to_sipm;
+ if (null == this.type) {
+ System.out.print("Null hit type, cannot convert tdc to time.");
+ return 1;
+ } else {
+ switch (this.type) {
+ case "bar up" -> {
+ tdc2time = Parameters.TDC2TIME;
+ veff = Parameters.VEFF;
+ //The distance will be computed at barhit level when y information is available
+ distance_to_sipm = 0;
+ }
+ case "bar down" -> {
+ tdc2time = Parameters.TDC2TIME;
+ veff = Parameters.VEFF;
+ //The distance will be computed at barhit level when y information is available
+ distance_to_sipm = 0;
+ }
+ case "bar" -> {
+ System.out.print("Bar hit type, cannot convert tdc to time.");
+ return 1;
+ }
+ default -> {
+ System.out.print("Undefined hit type, cannot convert tdc to time.");
+ return 1;
+ }
+ }
+ }
+ //Hit time. Will need implementation of offsets.
+ this.time = tdc2time * this.tdc - distance_to_sipm / veff;
+ return 0;
+ }
+
+ /**
+ * Converts ToT to energy (MeV). Sets the hit energy parameter to a raw
+ * energy for up/down bar hits.
+ *
+ * @return 0 if the energy was successfully set, or 1 if the hit type is
+ * unsupported.
+ */
+ public final int convertTotToEnergy() {
+ double tot2energy;
+ if (null == this.type) {
+ System.out.print("Null hit type, cannot convert tot to energy.");
+ return 1;
+ } else {
+ switch (this.type) {
+ case "bar up" -> {
+ tot2energy = Parameters.TOT2ENERGY;
+ //only half the information in the bar,
+ //the attenuation will be computed when the full hit is formed
+ this.energy = tot2energy * this.tot;
+ }
+ case "bar down" -> {
+ tot2energy = Parameters.TOT2ENERGY;
+ //only half the information in the bar,
+ //the attenuation will be computed when the full hit is formed
+ this.energy = tot2energy * this.tot;
+ }
+ case "bar" -> {
+ System.out.print("Bar hit type, cannot convert tot to energy.");
+ return 1;
+ }
+ default -> {
+ System.out.print("Undefined hit type, cannot convert tot to energy.");
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Calculates spatial coordinates (mm) for the hit based on row and column number of the bar within a sector.
+ * The row and column variables are obtained from the bank information.
+ *
+ * @return 0 if the coordinates were successfully set, or 1 if the hit type
+ * is undefined or unsupported.
+ */
+ public final int calculateXYZ() {
+
+ // Constants for positioning
+ int nRows = Parameters.NROWS;
+ double y_start = -(Parameters.LENGTH - Parameters.LONG_BAR_LENGTH)/2; // Starting Y position
+ double x_spacing = Parameters.BAR_WIDTH;
+ double x_start = -(Parameters.WIDTH - x_spacing)/2; // starting X position
+ double dy_long = Parameters.LONG_BAR_LENGTH;
+ double dy_short = Parameters.SHORT_BAR_LENGTH;
+
+ //Position calculation
+ double z_pos = 0;
+ double x_pos = x_start + ((this.column-1) * x_spacing);
+
+ double y_pos;
+ if(this.row-1 < (nRows - 1) / 2)
+ {
+ y_pos = y_start + ((this.row-1) * dy_long);
+ }
+ else if (this.row-1 == (nRows - 1) / 2) // middle row
+ {
+ y_pos = 0;
+ }
+ else
+ {
+ y_pos = y_start + (this.row -2) * dy_long + dy_short;
+ }
+
+ double[] localCoords = {x_pos, y_pos, z_pos};
+
+ // Calculate center coordinates for the sector
+ double sector_x = (-1+(this.sector-1)*2)*(Parameters.RADIUS)*Math.sin(Math.toRadians(Parameters.HORIZONTAL_OPENING_ANGLE/2+Parameters.HORIZONTAL_STARTING_ANGLE));
+ double sector_y = 0;
+ double sector_z = Parameters.RADIUS*Math.cos(Math.toRadians(Parameters.HORIZONTAL_OPENING_ANGLE/2+Parameters.HORIZONTAL_STARTING_ANGLE));
+
+ // Global coordinates of the sector
+ double[] globalCoordsSector = {sector_x, sector_y, sector_z};
+
+ // Rotation angle in radians
+ double thetaY = 0;
+
+ if(this.sector==1) thetaY = Math.toRadians(-(Parameters.HORIZONTAL_OPENING_ANGLE/2+Parameters.HORIZONTAL_STARTING_ANGLE));
+ if(this.sector==2) thetaY = Math.toRadians(Parameters.HORIZONTAL_OPENING_ANGLE/2+Parameters.HORIZONTAL_STARTING_ANGLE);
+
+ // Rotation matrix around the Y-axis
+ double[][] Ry = {
+ {Math.cos(thetaY), 0, Math.sin(thetaY)},
+ {0, 1, 0},
+ {-Math.sin(thetaY), 0, Math.cos(thetaY)}
+ };
+
+ // Rotate local coordinates
+ double[] rotatedCoords = new double[3];
+ for (int i = 0; i < 3; i++) {
+ rotatedCoords[i] = Ry[i][0] * localCoords[0] + Ry[i][1] * localCoords[1] + Ry[i][2] * localCoords[2];
+ }
+
+ // Calculate global coordinates for the hit
+ double[] globalCoordsBar = new double[3];
+ for (int i = 0; i < 3; i++) {
+ globalCoordsBar[i] = globalCoordsSector[i] + rotatedCoords[i];
+ }
+
+
+ this.x = globalCoordsBar[0];
+ this.y = globalCoordsBar[1];
+ this.z = globalCoordsBar[2];
+ return 0;
+ }
+
+ /**
+ * Compares two RECOILTOFHit objects to check if they match in the bar.
+ *
+ * - If the sector or row or column of the two hits do not match, the method
+ * returns {@code false}.
+ * - If both hits are in the same SiPM (i.e., their order is the same), or
+ * have incorrect order, the method returns {@code false}.
+ *
+ * If none of these conditions are violated, the method returns
+ * {@code true}, indicating the two hits match.
+ *
+ * @param hit2match The RECOILTOFHit object to compare with the current instance.
+ * @return {@code true} if the hits match; {@code false} otherwise.
+ */
+ public boolean matchBar(RECOILTOFHit hit2match) {
+ if (this.getSector() != hit2match.getSector()) {
+ //Two hits in different sectors
+ return false;
+ } else if (this.getRow() != hit2match.getRow()) {
+ //Two hits in different rows
+ return false;
+ } else if (this.getColumn() != hit2match.getColumn()) {
+ //Two hits in different columns
+ return false;
+ } else if (this.getOrder() > 1 || hit2match.getOrder() > 1) {
+ //At least one hit has incorrect order
+ return false;
+ } else {
+ //Match if one is order 0 and the other is order 1
+ return this.getOrder() != hit2match.getOrder();
+ }
+ }
+
+ /**
+ * Computes the azimuthal angle (phi) of the hit in rad.
+ *
+ * @return The azimuthal angle (phi) in radians, in the range [-π, π].
+ */
+ public double getPhi() {
+ return Math.atan2(this.y, this.x);
+ }
+
+ /**
+ * Constructor for a hit in the recoil tof. Initializes the hit's sector, row,
+ * column, order, TDC, ToT. Sets the hit's initial state regarding
+ * clustering. Set up the hit's type, time, energy, and spatial coordinates.
+ *
+ * @param sector The sector of the detector where the hit occurred.
+ * @param row The row of the detector where the hit was detected.
+ * @param column The column within the row that registered the hit.
+ * @param order Order of the hit.
+ * @param tdc TDC value.
+ * @param tot ToT value.
+ *
+ */
+ public RECOILTOFHit(int sector, int row, int column, int order, int tdc, int tot) {
+ this.sector = sector;
+ this.row = row;
+ this.column = column;
+ this.order = order;
+ this.tdc = tdc;
+ this.tot = tot;
+ this.isInACluster = false;
+
+ this.makeType();
+ this.convertTdcToTime();
+ this.convertTotToEnergy();
+ this.calculateXYZ();
+ }
+
+ public RECOILTOFHit() {
+ }
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ }
+}
diff --git a/reconstruction/recoiltof/src/main/java/org/jlab/service/recoiltof/RECOILTOFEngine.java b/reconstruction/recoiltof/src/main/java/org/jlab/service/recoiltof/RECOILTOFEngine.java
new file mode 100644
index 0000000000..bb8daa34a9
--- /dev/null
+++ b/reconstruction/recoiltof/src/main/java/org/jlab/service/recoiltof/RECOILTOFEngine.java
@@ -0,0 +1,88 @@
+package org.jlab.service.recoiltof;
+
+import java.util.ArrayList;
+
+import org.jlab.clas.reco.ReconstructionEngine;
+import org.jlab.io.base.DataBank;
+import org.jlab.io.base.DataEvent;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import org.jlab.detector.calib.utils.DatabaseConstantProvider;
+import org.jlab.io.hipo.HipoDataSource;
+import org.jlab.rec.recoiltof.banks.RecoBankWriter;
+import org.jlab.rec.recoiltof.cluster.RECOILTOFCluster;
+import org.jlab.rec.recoiltof.cluster.ClusterFinder;
+import org.jlab.rec.recoiltof.hit.RECOILTOFHit;
+import org.jlab.rec.recoiltof.hit.BarHit;
+import org.jlab.rec.recoiltof.hit.HitFinder;
+
+/**
+ * Service to return reconstructed RECOILTOF hits and clusters
+ *
+ * @author npilleux, Nilanga Wickramaarachchi
+ *
+ */
+public class RECOILTOFEngine extends ReconstructionEngine {
+
+ public RECOILTOFEngine() {
+ super("RECOILTOF", "Nilanga Wickramaarachchi", "1.0");
+ }
+
+ RecoBankWriter rbc;
+
+ private final AtomicInteger run = new AtomicInteger(0);
+
+ @Override
+ public boolean processDataEvent(DataEvent event) {
+
+ if (!event.hasBank("RUN::config")) {
+ return true;
+ }
+
+ DataBank bank = event.getBank("RUN::config");
+
+ int newRun = bank.getInt("run", 0);
+ if (newRun == 0) {
+ return true;
+ }
+
+ if (run.get() == 0 || (run.get() != 0 && run.get() != newRun)) {
+ run.set(newRun);
+ }
+
+
+ //Hit finder init
+ HitFinder hitfinder = new HitFinder();
+ hitfinder.findHits(event);
+
+ ArrayList BarHits = hitfinder.getBarHits();
+
+ //Exit if hit list is empty
+ if (BarHits.isEmpty()) {
+ // System.out.println("No hits : ");
+ // event.show();
+ return true;
+ }
+
+ ClusterFinder clusterFinder = new ClusterFinder();
+ clusterFinder.makeClusters(event,hitfinder);
+ ArrayList Clusters = clusterFinder.getClusters();
+
+ if (BarHits.size() != 0) {
+ rbc.appendRECOILTOFBanks(event, BarHits, Clusters);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean init() {
+ rbc = new RecoBankWriter();
+
+ this.registerOutputBank("RECOILTOF::hits", "RECOILTOF::clusters");
+
+ return true;
+ }
+
+ public static void main(String arg[]) {
+ }
+}