27 from lsst.meas.base import SingleFramePlugin, SingleFramePluginConfig
28 from lsst.meas.base import FlagHandler, FlagDefinitionList, SafeCentroidExtractor
31 __all__ = (
"SingleFrameNaiveTrailConfig",
"SingleFrameNaiveTrailPlugin")
35 """Config class for SingleFrameNaiveTrailPlugin.
40 @register(
"ext_trailedSources_Naive")
42 """Naive trailed source measurement plugin
44 Measures the length, angle from +x-axis, and end points of an extended
45 source using the second moments.
49 config: `SingleFrameNaiveTrailConfig`
53 schema: `lsst.afw.table.Schema`
54 Schema for the output catalog.
55 metadata: `lsst.daf.base.PropertySet`
56 Metadata to be attached to output catalog.
60 This measurement plugin aims to utilize the already measured shape second
61 moments to naively estimate the length and angle, and thus end-points, of a
62 fast-moving, trailed source. The estimate for the trail length is
63 obtained by doubling the semi-major axis, a, of the ellipse defined by
64 the second moments. The angle, theta, from the x-axis is computed
65 similarly (via second moments). The end points of the trail are then given
66 by (xc +/- a*cos(theta), yc +/- a*sin(theta)), with xc and yc being the
71 lsst.meas.base.SingleFramePlugin
74 ConfigClass = SingleFrameNaiveTrailConfig
80 return cls.APCORR_ORDER + 0.1
82 def __init__(self, config, name, schema, metadata):
83 super().
__init__(config, name, schema, metadata)
86 self.
keyX0keyX0 = schema.addField(name +
"_x0", type=
"D", doc=
"Trail head X coordinate.", units=
"pixel")
87 self.
keyY0keyY0 = schema.addField(name +
"_y0", type=
"D", doc=
"Trail head Y coordinate.", units=
"pixel")
88 self.
keyX1keyX1 = schema.addField(name +
"_x1", type=
"D", doc=
"Trail tail X coordinate.", units=
"pixel")
89 self.
keyY1keyY1 = schema.addField(name +
"_y1", type=
"D", doc=
"Trail tail Y coordinate.", units=
"pixel")
90 self.
keyFluxkeyFlux = schema.addField(name +
"_flux", type=
"D", doc=
"Trailed source flux.", units=
"count")
91 self.
keyLkeyL = schema.addField(name +
"_length", type=
"D", doc=
"Trail length.", units=
"pixel")
92 self.
keyAnglekeyAngle = schema.addField(name +
"_angle", type=
"D", doc=
"Angle measured from +x-axis.")
95 self.
keyX0ErrkeyX0Err = schema.addField(name +
"_x0Err", type=
"D",
96 doc=
"Trail head X coordinate error.", units=
"pixel")
97 self.
keyY0ErrkeyY0Err = schema.addField(name +
"_y0Err", type=
"D",
98 doc=
"Trail head Y coordinate error.", units=
"pixel")
99 self.
keyX1ErrkeyX1Err = schema.addField(name +
"_x1Err", type=
"D",
100 doc=
"Trail tail X coordinate error.", units=
"pixel")
101 self.
keyY1ErrkeyY1Err = schema.addField(name +
"_y1Err", type=
"D",
102 doc=
"Trail tail Y coordinate error.", units=
"pixel")
105 flagDefs.addFailureFlag(
"No trailed-source measured")
106 self.
NO_FLUXNO_FLUX = flagDefs.add(
"flag_noFlux",
"No suitable prior flux measurement")
107 self.
flagHandlerflagHandler = FlagHandler.addFields(schema, name, flagDefs)
112 """Run the Naive trailed source measurement algorithm.
116 measRecord : `lsst.afw.table.SourceRecord`
117 Record describing the object being measured.
118 exposure : `lsst.afw.image.Exposure`
119 Pixel data to be measured.
123 lsst.meas.base.SingleFramePlugin.measure
126 Ixx, Iyy, Ixy = measRecord.getShape().getParameterVector()
131 a = np.sqrt(0.5 * (xpy + np.sqrt(xmy2 + 4.0*xy2)))
134 theta = 0.5 * np.arctan2(2.0 * Ixy, xmy)
135 dydt = a*np.cos(theta)
136 dxdt = a*np.sin(theta)
143 F = measRecord.get(
"base_SdssShape_instFlux")
146 if not np.isfinite(F):
147 if np.isfinite(measRecord.getApInstFlux()):
148 F = measRecord.getApInstFlux()
153 xcErr2, ycErr2 = np.diag(measRecord.getCentroidErr())
154 IxxErr2, IyyErr2, IxyErr2 = np.diag(measRecord.getShapeErr())
155 desc = np.sqrt(xmy2 + 4.0*xy2)
156 denom = 2*np.sqrt(2.0*(Ixx + np.sqrt(4.0*xy2 + xmy2 + Iyy)))
157 dadIxx = (1.0 + (xmy/desc)) / denom
158 dadIyy = (1.0 - (xmy/desc)) / denom
159 dadIxy = (4.0*Ixy) / (desc * denom)
160 aErr2 = IxxErr2*dadIxx*dadIxx + IyyErr2*dadIyy*dadIyy + IxyErr2*dadIxy*dadIxy
161 thetaErr2 = ((IxxErr2 + IyyErr2)*xy2 + xmy2*IxyErr2) / (desc*desc*desc*desc)
165 xErr2 = aErr2*dxda*dxda + thetaErr2*dxdt*dxdt
166 yErr2 = aErr2*dyda*dyda + thetaErr2*dydt*dydt
167 x0Err = np.sqrt(xErr2 + xcErr2)
168 y0Err = np.sqrt(yErr2 + ycErr2)
171 measRecord.set(self.
keyX0keyX0, x0)
172 measRecord.set(self.
keyY0keyY0, y0)
173 measRecord.set(self.
keyX1keyX1, x1)
174 measRecord.set(self.
keyY1keyY1, y1)
175 measRecord.set(self.
keyFluxkeyFlux, F)
176 measRecord.set(self.
keyLkeyL, L)
177 measRecord.set(self.
keyAnglekeyAngle, theta)
178 measRecord.set(self.
keyX0ErrkeyX0Err, x0Err)
179 measRecord.set(self.
keyY0ErrkeyY0Err, y0Err)
180 measRecord.set(self.
keyX1ErrkeyX1Err, x0Err)
181 measRecord.set(self.
keyY1ErrkeyY1Err, y0Err)
183 def fail(self, measRecord, error=None):
188 lsst.meas.base.SingleFramePlugin.fail
191 self.
flagHandlerflagHandler.handleFailure(measRecord)
193 self.
flagHandlerflagHandler.handleFailure(measRecord, error.cpp)
def getExecutionOrder(cls)
def __init__(self, config, name, schema, metadata)
def measure(self, measRecord, exposure)
def fail(self, measRecord, error=None)