<resource schema="gaia">
	<macDef name="pubDIDBase">ivo://\getConfig{ivoa}{authority}/~?\rdId/</macDef>
	<macDef name="zeropoints">{
		"G": 25.6884,
		"BP": 25.3514,
		"RP": 24.7619,
	}</macDef>

	<meta name="title">Selections from Gaia Data Release 2 (DR2)</meta>
	<meta name="description" format="rst">
		This schema contains data re-published from the official
		Gaia mirrors (such as ivo://uni-heidelberg.de/gaia/tap) either to
		support combining its data with local tables (the various Xlite tables)
		or to make the data more accessible to VO clients (e.g., epoch fluxes).

		Other Gaia-related data is found in, among others, the gdr2dist, gdr3mock,
		gdr3spec, gedr3auto, gedr3dist, gedr3mock, and gedr3spur schemas.
	</meta>
	<meta name="creator">
		<meta name="name">GAIA Collaboration</meta>
	</meta>
	<meta name="content">
		<meta name="type">Catalog</meta>
	</meta>
	<meta name="coverage.waveband">Optical</meta>
	<meta name="creationDate">2018-03-13T08:44:00Z</meta>
	<meta name="schema-rank">50</meta>

	<meta name="subject">stars</meta>
	<meta name="subject">surveys</meta>
	<meta name="subject">astrometry</meta>
	<meta name="subject">proper-motions</meta>
	<meta name="subject">time-domain-astronomy</meta>


	<meta name="facility">Gaia</meta>

	<meta name="_news" author="MD" date="2018-12-05" role="updated">
		Added a column with the RUWE as a consistency measure.
	</meta>

	<meta name="copyright" format="rst">
		If you use public Gaia DR2 data in a paper, please take note of
		`ESAC's guide`_ on how to acknowledge and cite it.

		.. _ESAC's guide: http://gea.esac.esa.int/archive/documentation/GDR2/Miscellaneous/sec_credit_and_citation_instructions/
	</meta>

	<coverage>	
		<temporal>2015-06-01 2015-06-01</temporal>
		<spatial>0/0-11</spatial>
		<spectral>1.986e-19 4.966e-19</spectral>
	</coverage>

	<STREAM id="dr2lightcolumns">
		<stc>
			Position ICRS BARYCENTER SPHER3 Epoch J2015.5 "ra" "dec" "parallax"
				Velocity "pmra" "pmdec" 0
		</stc>

		<column name="source_id" type="bigint"
			ucd="meta.id;meta.main"
			tablehead="Source Id"
			description="Unique source identifier.  Note that this *cannot*
				be matched against the DR1 source_id."
			verbLevel="1" note="id">
			<values nullLiteral="-1"/>
			<property name="statisticsTarget">10000</property>
		</column>
		<column name="ra" type="double precision"
			ucd="pos.eq.ra;meta.main" unit="deg"
			tablehead="RA (ICRS)"
			description="Barycentric Right Ascension in ICRS at ref_epoch"
			verbLevel="1">
			<property name="statisticsTarget">10000</property>
		</column>
		<column name="dec" type="double precision"
			ucd="pos.eq.dec;meta.main" unit="deg"
			tablehead="Dec (ICRS)"
			description="Barycentric Declination in ICRS at ref_epoch"
			verbLevel="1">
			<property name="statisticsTarget">10000</property>
		</column>
		<column name="ra_error"
			ucd="stat.error;pos.eq.ra" unit="mas"
			tablehead="Err. RA"
			description="Standard error of ra (with cos δ applied)."/>
		<column name="dec_error"
			ucd="stat.error;pos.eq.dec" unit="mas"
			tablehead="Err. Dec"
			description="Standard error of dec" />

		<column name="pmra"
			ucd="pos.pm;pos.eq.ra" unit="mas/yr"
			tablehead="µ(RA)"
			description="Proper motion in right ascension of the source in ICRS
				at ref_epoch. This is the projection of the proper motion vector in the
				direction of increasing right ascension."
			verbLevel="1"/>
		<column name="pmdec"
			ucd="pos.pm;pos.eq.dec" unit="mas/yr"
			tablehead="µ(Dec)"
			description="Proper motion in declination at ref_epoch."
			verbLevel="1"/>
		<column name="pmra_error"
			ucd="stat.error;pos.pm;pos.eq.ra" unit="mas/yr"
			tablehead="Err. PM(RA)"
			description="Standard error of pmra"/>
		<column name="pmdec_error"
			ucd="stat.error;pos.pm;pos.eq.dec" unit="mas/yr"
			tablehead="Err. PM(Dec)"
			description="Standard error of pmdec"/>

		<column name="parallax"
			ucd="pos.parallax" unit="mas"
			tablehead="Parallax"
			description="Absolute barycentric stellar parallax of the source at the
			reference epoch ref_epoch.  If looking for a distance, consider joining
			with gdr2dist.main and using the distances from there."
			verbLevel="1"/>
		<column name="parallax_error"
			ucd="stat.error;pos.parallax" unit="mas"
			tablehead="Parallax_error"
			description="Standard error of parallax" />

		<column name="phot_g_mean_mag"
			ucd="phot.mag;em.opt;stat.mean" unit="mag"
			tablehead="m_G"
			description="Mean magnitude in the G band. This is computed from the
				G-band mean flux applying the magnitude zero-point in the Vega
				scale."
			verbLevel="1" note="phot">
			<property name="statisticsTarget">5000</property>
		</column>
		<column name="phot_g_mean_flux" type="double precision"
			ucd="phot.flux;em.opt;stat.mean" unit="s**-1"
			tablehead="Flux_G"
			description="G-band mean flux as electrons per second."
			note="phot"/>
		<column name="phot_g_mean_flux_error"
			ucd="stat.error;phot.flux;em.opt;stat.mean" unit="s**-1"
			tablehead="Err. Flux(G)"
			description="Error on phot_g_mean_flux"/>

		<LOOP>
			<csvItems>
				band, ucd
				rp,   R
				bp,   B
			</csvItems>
			<events>
				<column name="phot_\band\+_mean_flux" type="double precision"
					unit="s**-1" ucd="phot.flux;em.opt.\ucd"
					tablehead="Flux \upper{\band}"
					description="Mean flux in the integrated \upper{\band} band."
					note="phot"/>
				<column name="phot_\band\+_mean_flux_error"
					unit="s**-1" ucd="stat.error;phot.flux;em.opt.\ucd"
					tablehead="Err. Fl. \upper{\band}"
					description="Error in the mean flux in the integrated \upper{\band}
						band. Errors are computed from the dispersion about the weighted
						mean of the input calibrated photometry."/>
				<column name="phot_\band\+_mean_mag"
					unit="mag" ucd="phot.mag;em.opt.\ucd"
					tablehead="Mag \upper{\band}"
					description="Mean magnitude in the integrated \upper{\band} band.
						This is computed from the \upper{\band}-band mean flux
						applying the magnitude zero-point in the Vega scale.
						No error is provided for this quantity as the error
						distribution is only symmetric in flux space.  For errors
						small compared to the flux (less than 10%, say), the magnitude
						error is well approximated by 1.09*flux/flux_err."
					verbLevel="1" note="phot">
					<property name="statisticsTarget">5000</property>
				</column>
			</events>
		</LOOP>
		<column name="phot_bp_rp_excess_factor"
			ucd="stat.fit.goodness"
			tablehead="BP/RP excess"
			description="BP/RP excess factor estimated from the comparison of the sum
				of integrated BP and RP fluxes with respect to the flux in the G band.
				This measures the excess of flux in the BP and RP integrated photometry
				with respect to the G band. This excess is believed to be caused by
				background and contamination issues affecting the BP and RP data.
				Therefore a large value of this factor for a given source indicates
				systematic errors in the BP and RP photometry."/>

		<column name="radial_velocity"
			unit="km/s" ucd="spect.dopplerVeloc"
			tablehead="RV"
			description="Spectroscopic radial velocity in the solar barycentric
				reference frame. The radial velocity provided is the median value of the
				radial velocity measurements at all epochs.  Warning: in the vicinity
				of bright stars, DR2 RVs can be grossly wrong.  See arXiv:1901.10460
				for details."
				verbLevel="1"/>
		<column name="radial_velocity_error"
			unit="km/s" ucd="stat.error;spect.dopplerVeloc"
			tablehead="Err. RV"
			description="The radial velocity error is the error on the
				median to which a constant noise floor of 0.11 km/s has been
				added in quadrature to take into account the calibration
				contribution."/>

		<column name="astrometric_gof_al"
			ucd="stat.fit.goodness"
			tablehead="GoF"
			description="Goodness-of-fit statistic of the astrometric solution for
				the source in the along-scan direction (you probably want to use
				RUWE instead of this)."
			note="gof"/>
		<column name="astrometric_params_solved" type="smallint"
			ucd="meta.code"
			tablehead="PS"
			description="This is a binary code indicating which astrometric
				parameters were estimated for the source. A set bit means the
				parameter was estimated. The least-significant bit represents α,
				the next bits δ, parallax, PM(RA) and PM(De). For Gaia DR2 the
				only relevant values are 31 (all five parameters solved) and 3
				(only positions).">
			<values nullLiteral="-1"/>
		</column>

		<column
			name="random_index" type="bigint" required="True"
			ucd="meta.code"
			tablehead="Random"
			description="Random index that can be used to deterministically
				select subsets"/>

		<meta name="note" tag="gof">
			This is the “gaussianized chi-square”, which for good fits should
			approximately follow a normal distribution with zero mean value
			and unit standard deviation. Values exceeding, say, +3 thus indicate
			a bad fit to the data.

			This statistic is computed according to the formula
			
				gof = sqrt(9ν/2) [(χ²/ν)^(1/3) + 2/(9ν) − 1],

			where χ² (``astrometric_chi2_al``) is the AL chi-square statistic and
			ν = (``astrometric_n_good_obs_al`` − 5) is the number of degrees of
			freedom for a source update. Here, 5 is the number of astrometric
			parameters.  Note that only “good” (i.e. not strongly downweighted)
			observations are included in χ² and ν.

			An alternative indicator of bad fits is the astrometric excess noise. In
			AGIS the source update deals with bad fits by adding astrometric excess
			noise to the formal observation noise. This reduces the weight of the
			observations and inflates the covariance of the estimated astrometric
			parameters correspondingly. However, the chi-square values used to
			calculate ``astrometric_gof_al`` do not take into account the astrometric
			excess noise, and ``astrometric_gof_al`` can therefore always be used as
			a goodness-of-fit indicator of the source solution in AGIS.
		</meta>

		<meta name="note" tag="id">
			For the contents of Gaia DR2,
			the source ID consists of a 64-bit integer, least
			significant bit = 1 and most significant bit = 64, comprising:

			* a HEALPix index number (sky pixel) in bits 36 - 63; by definition the
				smallest HEALPix index number is zero.
			* a 2-bit Data Processing Centre code in bits 33 - 35; for example
				MOD(source_id / 4294967296, 8) can be used to distinguish between
				sources initialised via the Initial Gaia Source List by the Torino DPC
				(code = 0) and sources otherwise detected and assigned by Gaia
				observations (code &gt; 0)
			* a 25-bit plus 7 bit sequence number within the HEALPix pixel in bits 1
				to 32 split into:

				* a 25 bit running number in bits 8 - 32; the running numbers are
					defined to be positive, i.e. never zero (except in the case of forced
					empty windows)
				* a 7-bit component number in bits 1 - 7

			This means that the HEALpix index level 12 of a given source is contained
			in the most significant bits. HEALpix index of 12 and lower levels can
			thus be retrieved as follows:

			* HEALpix level 12 = source_id / 34359738368
			* HEALpix level 11 = source_id / 137438953472
			* HEALpix level 10 = source_id / 549755813888
			* HEALpix level n = source_id / 2^35 * 4^(12 - level).
		</meta>

		<meta name="note" tag="phot">
			Note that several systematics have been found for both Gaia G and
			BP photometry on the level of 10s of mmags.  See
			:bibcode:`2018A&amp;A...616A..17A` and :bibcode:`2018A&amp;A...616A...4E`
			for a description of the problem as well as some proposed empirical
			corrections.  For sources brighter than 16.5, see also
			:bibcode:`2018MNRAS.479L.102C` and :bibcode:`2018A&amp;A...617A.138W`,
			where the latter also discusses systematics in BP.
		</meta>
	</STREAM>


	<table id="dr2light" onDisk="True" primary="source_id"
			mixin="//scs#q3cindex" adql="True" nrows="1600000000">

		<meta name="title">Gaia DR2 source catalogue "light"</meta>
		<meta name="description" format="rst">
			This is a “light” version of the full Gaia DR2 gaia_source table,
			containing the original astrometric and photmetric columns with just
			enough additional information to let careful researchers notice when data
			is becomes uncertain and the full error model should be consulted.  The
			full DR2 is available from numerous places in the VO (in particular from
			the TAP services ivo://uni-heidelberg.de/gaia/tap and
			ivo://esavo/gaia/tap).

			This table also includes a column containing the Renormalized Unit Weight
			Error RUWE (GAIA-C3-TN-LU-LL-124-01), a robust measure for the
			consistency of the solution.
			
			On this TAP service, there is the table gdr2dist.main containing
			distances computed by Bailer-Jones et al (:bibcode:`2018AJ....156...58B`).
			If in doubt, use these instead of the parallaxes provided here.
		</meta>

		<meta name="continues" ivoId="ivo://org.gavo.dc/gaia/q/dr1"
			>Gaia Data Release 1 (DR1) gaia_source</meta>

		<index columns="pmra"/>
		<index columns="pmdec"/>
		<index columns="parallax"/>
		<index columns="phot_g_mean_mag"/>
		<index columns="phot_bp_mean_mag"/>
		<index columns="phot_rp_mean_mag"/>
		<FEED source="dr2lightcolumns">
			<!-- unfortunately, astrometric_params_solved had the wrong
			type during the original import.  This seems to minor to
			justify a big rewrite now, so we just fudge it. -->
			<EDIT ref="column[astrometric_params_solved]">
				<type>real</type>
			</EDIT>
		</FEED>
		<publish sets="ivo_managed,local"/>
		
		<column name="ruwe"
			ucd="stat.weight"
			tablehead="RUWE"
			description="Renormalized Unit Weight Error; this is a revised
				measure for the overall consistency of the solution as defined
				by GAIA-C3-TN-LU-LL-124-01.  A suggested cut on this is
				RUWE &lt;1.40) See the note for details."
			verbLevel="5" note="ruwe"/>
		
		<meta name="note" tag="ruwe"><![CDATA[
			The Renormalized Unit Weight Error (RUWE) is defined in
			L. Lindegren: Re-normalising the astrometric chi-square in Gaia DR2
			(Gaia tech note `GAIA-C3-TN-LU-LL-124-01
			<http://www.rssd.esa.int/doc_fetch.php?id=3757412>`_).  This is
			essentially the astrometric_chi2 from the DR2 release, but corrected
			for its strong dependency on magnitude and colour.  For quality
			cuts, this is the recommended quantity to use.  Lindegren suggests
			using RUWE<1.4 as a quality cut for “good” solutions based on
			the shape of the distribution.

			The values given here were calculated independently of DPAC and do
			not use interpolation on the grids provided by Lindegren et al.  Thus,
			minor differences from the “official” RUWE values are expected.
			As Lindegren et al point out, such differences should not matter
			in science applications, though.
		]]></meta>
	</table>

	<data id="import">
		<sources pattern="data2/dump.txt.gz"/>
		<directGrammar id="booster"
			cBooster="res/booster2func.c"
			type="split"
			autoNull="\N"
			splitChar="\t"
			preFilter="zcat">
		</directGrammar>
		<make table="dr2light"/>
	</data>

	<!-- ##################### start RUWE fiddling
		If fixing an existing non-ruwe table, just run
		alter table gaia.dr2light add column ruwe real
		before importing this.
		-->

	<table id="ruwes" onDisk="True"> <!-- temporary="True"> -->
		<meta name="description">An internal table temporarily used to
			insert RUWEs into dr2light.  Nonexisting almost always.</meta>
		<index columns="source_id"/>
		<column original="dr2light.source_id">
			<property name="statisticsTarget">10000</property>
		</column>
		<column original="dr2light.ruwe"/>
	</table>

	<data id="import_ruwes" auto="False">
		<sources pattern="data2/ruwes.txt.gz"/>
		<directGrammar id="ruweboost"
			cBooster="res/ruwebooster.c"
			type="split"
			splitChar=" "
			preFilter="zcat"/>
		<make table="ruwes"/>
	</data>

	<!-- import this with the -R flag or it'll run forever -->
	<data id="insert_ruwes" updating="True" auto="False">
		<make table="dr2light">
			<script name="insert RUWES" lang="SQL" type="postCreation">
				set enable_seqscan=False;
				UPDATE gaia.dr2light as g
				SET ruwe=mat.ruwe
				FROM gaia.ruwes as mat
				WHERE
					g.source_id=mat.source_id
					AND g.source_id&lt;1000000000000000;
			</script>
		</make>
	</data>


	<!-- ##################### end RUWE fiddling -->

	<!--  #################### start epoch photometry -->
	<table id="dr2epochflux" onDisk="True" adql="True" primary="source_id">
		<meta name="title">Gaia DR2 epoch fluxes</meta>
		<meta name="description" format="rst">
			A table of the light curves released with Gaia DR2 (about half a million
			in total).  In each Gaia band (G, BP, RP), we give epochs, fluxes and
			their errors in arrays.  We do not include the quality flags (DR2: “may
			be safely ignored for many general purpose applications”).  You can
			access them through the associated datalink service if you select
			source_id.  You will usually join this table with gaia.dr2light.

			We have also removed all entries with NaN observation times; hence,
			the array lengths in the different bands can be significantly different,
			and the indices in transit_ids do not always correspond to the
			indices in the time series.

			Furthermore, we only give fluxes and their errors here rather than
			magnitudes.  Fluxes can be turned into magnitude using::
			
				mag = -2.5 log10(flux)+zero point,

			where the zero points assumed for Gaia DR2 are
			25.6884±0.0018 in G, 25.3514±0.0014 in BP, and
			24.7619±0.0019 in RP (VEGAMAG).
		</meta>

		<meta name="_associatedDatalinkService">
			<meta name="serviceId">tsdl</meta>
			<meta name="idColumn">source_id</meta>
		</meta>

		<publish sets="ivo_managed,local"/>

		<foreignKey source="source_id" inTable="dr2light" metaOnly="True"/>
		<column original="dr2light.source_id" note="sid"/>
		<column name="transit_id" type="bigint[]"
			ucd="meta.version"
			tablehead="T. ids"
			description="Transit unique identifier. For a given object, a transit
				comprises the different Gaia observations (SM, AF, BP, RP and RVS)
				obtained for each focal plane crossing.  NOTE: Where invalid
				observations have been removed, transit_id *cannot* be joined
				with times and fluxes."/>
		<column name="g_transit_time" type="double precision[]"
			unit="d" ucd="time.epoch"
			tablehead="Epochs for G fluxes"
			description="The field-of-view transit averaged observation times for
				the G-band observations.  This is given for barycentric TCB in
				JD-2,455,197.5."
			note="t"/>
		<column name="g_transit_flux" type="real[]"
			unit="s**-1" ucd="phot.flux;em.opt.V"
			tablehead="G fluxes"
			description="G-band fluxes for the transit, for combination
				with g_transit_time_mjd.  Here, this is
				a combination of individual SM-AF CCD fluxes"/>
		<column name="g_transit_flux_error" type="real[]"
			unit="s**-1" ucd="stat.error;phot.flux;em.opt.V"
			tablehead="Errors in G fluxes"
			description="The error in G-band flux."/>

		<LOOP>
			<csvItems>
				band, ucd
				rp,   R
				bp,   B
			</csvItems>
			<events>
				<column name="\band\+_obs_time" type="double precision[]"
					unit="d" ucd="time.epoch"
					tablehead="Epochs for \upper{\band} fluxes"
					description="The observation time of the \upper{\band} CCD transit.
						This is given for barycentric TCB in JD-2,455,197.5."
					note="t"/>
				<column name="\band\+_flux" type="real[]"
					unit="s**-1" ucd="phot.flux;em.opt.\ucd"
					tablehead="\upper{\band} fluxes"
					description="\upper{\band}-band integrated fluxes, for combination
						with \band\+_obs_time"/>
				<column name="\band\+_flux_error" type="real[]"
					unit="s**-1" ucd="stat.error;phot.flux;em.opt.\ucd"
					tablehead="Errors in \upper{\band} fluxes"
					description="The error in \upper{\band}-band flux."/>
			</events>
		</LOOP>

		<column name="solution_id" type="bigint" required="True"
			ucd="meta.version"
			tablehead="Sol. Id"
			description="A DPAC id for the toolchain used to produce these
				particular time series.  It is given to facilitate comparison
				with ESAC data products."/>

		<meta name="note" tag="sid">
			This is the Gaia DR source id.  You will usually use this
			no pull in object metadata from dr2light, like this::

				SELECT ...
				FROM gaia.dr2epochflux
				JOIN gaia.dr2light
					USING (source_id)
				WHERE ...
		</meta>
		<meta name="note" tag="t">
			Times here are computed as follows: First, the observation time is
			converted from On-board Mission Time (OBMT) into Julian date in TCB
			(Temps Coordonnée Barycentrique). Next, a correction is applied for the
			light-travel time to the Solar system barycentre, resulting in
			Barycentric Julian Date (BJD). Finally, an offset of 2,455,197.5 days is
			applied (corresponding to a reference time $T_0$ at 2010-01-01T00:00:00)
			to have a conveniently small numerical value. Although the centroiding
			time accuracy of the individual CCD observations is (much) below 1~ms
			(e.g. in BP and RP), the G band observation time is averaged over
			typically 9 CCD observations taken in a time range of about 44sec.
		</meta>
	</table>

	<procDef id="remove_nans_from_timeseries">
		<doc>This removes NaN obs times and their associated flux/errors
		</doc>
		<setup>
			<par name="dates_name"/>
			<par name="fluxes_name"/>
			<par name="errors_name"/>
			<code>
				import numpy as np
				import io
				
				def _parse(s):
					return np.genfromtxt(
						io.BytesIO(s),
						delimiter=',',
						autostrip=True,
						dtype=np.double)
			</code>
		</setup>
		<code>
			stuff = vars[dates_name][1:-1].strip()
			if not stuff:
				vars[fluxes_name] = vars[errors_name] = vars[dates_name] = None
				return

			dates = _parse(stuff)
			vars[fluxes_name] = _parse(vars[fluxes_name][1:-1])[dates==dates]
			vars[errors_name] = _parse(vars[errors_name][1:-1])[dates==dates]
			vars[dates_name] = dates[dates==dates]
		</code>
	</procDef>

	<data id="import_epochs">
		<sources pattern="data2/epoch_dump.txt.gz"/>
		<reGrammar fieldSep='\t'
			preFilter="zcat"
			names="source_id, transit_id, g_transit_time, g_transit_flux,
				g_transit_flux_error, bp_obs_time, bp_flux, bp_flux_error,
				rp_obs_time, rp_flux, rp_flux_error,  solution_id"/>
		<make table="dr2epochflux">
			<rowmaker idmaps="*">
				<var key="transit_id"
					>[int(a) for a in @transit_id[1:-1].split(",")]</var>

				<apply procDef="remove_nans_from_timeseries">
					<bind name="dates_name">"bp_obs_time"</bind>
					<bind name="fluxes_name">"bp_flux"</bind>
					<bind name="errors_name">"bp_flux_error"</bind>
				</apply>

				<apply procDef="remove_nans_from_timeseries">
					<bind name="dates_name">"rp_obs_time"</bind>
					<bind name="fluxes_name">"rp_flux"</bind>
					<bind name="errors_name">"rp_flux_error"</bind>
				</apply>

				<apply procDef="remove_nans_from_timeseries">
					<bind name="dates_name">"g_transit_time"</bind>
					<bind name="fluxes_name">"g_transit_flux"</bind>
					<bind name="errors_name">"g_transit_flux_error"</bind>
				</apply>
			</rowmaker>
		</make>
	</data>

	<STREAM id="time-series-template">
		<table id="instance_\band_short">
			<meta name="description">
				With data relase 2, the Gaia DPAC released lightcurves for objects
				considered variable.  This is such a lightcurve (with invalid
				points removed), for the \band_human band.

				Note that the magnitudes in phot are blindly converted
				to flux using the zero point given in the metadata.  If
				flux_error/flux&lt;0.1, you can use 1.09*flux_error/flux as a good
				estimate for the error; else the distribution is so skewed that you
				should work with fluxes rather than magnitude.

				The zero point in the magnitude metadata has been taken from
				the SVO filter profile service.
			</meta>

			<mixin	
				effectiveWavelength="\effective_wavelength"
				filterIdentifier='"\band_human"'
				magnitudeSystem="Vega"
				zeroPointFlux='"\zero_point"'
				longitude="@ra"
				latitude="@dec"
				phot_description="Magnitude in \band_human, Vega system."
				phot_ucd='phot.mag;\band_ucd'
				phot_unit="mag"
				pos_epoch="J2015.5"
				refframe="ICRS"
				refposition="BARYCENTER"
				time0="0"
				time_description="Barycentric time of observation"
				timescale="TCB"
				dependent_axes="[@phot @flux]"
			>//timeseries#phot-0</mixin>

			<dm>
				(phot:PhotCal) {
					filterIdentifier: "\band_human"
					zeroPointFlux: "\zero_point"
					magnitudeSystem: Vega
					effectiveWavelength: "\effective_wavelength"
					value: @flux
				}
			</dm>

			<dm>
				(ivoa:Measurement) {
					value: @flux
					statError: @flux_error
				}
			</dm>

			<dm>
				(ds:Dataset) {
					dataProductType: TIMESERIES
					title: @title
					curation:
						(ds:Curation) {
							calibLevel: 1
							publisher:
								(ds:Publisher) {
									name: "GAVO Data Center"
									publisherId: "ivo://org.gavo.dc"
								}
						}
					target:
						(ds:AstroTarget) {
							position: [@ra @dec @ssa_location]
						}
				}
			</dm>

			<param name="source_id" type="text"
				ucd="meta.id;meta.main"
				description="Gaia DR2 source_id of the object"/>
			<param name="title" type="text"
				ucd="meta.title;obs"
				description="Publisher-assigned title of the data set"/>
			<param name="ra" type="double precision"
				ucd="pos.eq.ra"
				description="Gaia DR2 RA of source object"/>
			<param name="dec" type="double precision"
				ucd="pos.eq.dec"
				description="Gaia DR2 Dec of source object"/>
			<param name="ssa_location" type="double precision[2]"
				ucd="pos.eq"
				xtype="point"
				description="Target position as per SSA (a DALI point)"/>

			<column name="obs_time" type="double precision"
				unit="d" ucd="time.epoch"
				description="Observation time (JD in barycentric TCB)."/>
			<column name="flux" type="real"
				unit="s**-1" ucd="phot.flux;\band_ucd"
				description="Integrated flux in \band_human.  This is in CCD
					electrons per second."/>
			<column name="flux_error" type="real"
				unit="s**-1" ucd="stat.error;phot.flux;\band_ucd"
				description="Error in \band_human flux."/>
		</table>
	</STREAM>

	<LOOP source="time-series-template">
		<csvItems>
			band_short, band_human, band_ucd, effective_wavelength, zero_point
			G,          GAIA/GAIA2.G,   em.opt,   6.23e-7,          2836.53
			BP,         GAIA/GAIA2.Gbp, em.opt.B, 5.05e-7,          3399.69
			RP,         GAIA/GAIA2.Grp, em.opt.R, 7.73e-7,          2489.74
		</csvItems>
	</LOOP>

	<data id="make_instance" auto="False">
		<embeddedGrammar>
			<!-- the source token is the datalink descriptor, with band
				monkeypatched in as per tsdl's dataFunction. -->
			<iterator>
				<code>
					epName, fluxName, errorName = {
						"G": ("g_transit_time", "g_transit_flux", "g_transit_flux_error"),
						"BP": ("bp_obs_time", "bp_flux", "bp_flux_error"),
						"RP": ("rp_obs_time", "rp_flux", "rp_flux_error"),
						}[self.sourceToken.band]

					zeropoint = \zeropoints[self.sourceToken.band]

					epochs, fluxes, errors = (
						self.sourceToken.row[epName],
						self.sourceToken.row[fluxName],
						self.sourceToken.row[errorName])

					labels = ["obs_time", "flux", "flux_error"]
					for tup in zip([e+2455197.5 for e in epochs], fluxes, errors):
						if tup[0]==tup[0]:
							res = dict(zip(labels, tup))
							res["phot"] = -2.5*math.log10(res["flux"])+zeropoint
							yield res
				</code>
			</iterator>

			<pargetter>
				<code>
					with base.getTableConn() as conn:
						res = list(conn.queryToDicts(
							"SELECT * FROM gaia.dr2light WHERE source_id=%(source_id)s"
								" LIMIT 2",
							{"source_id": self.sourceToken.sourceId}))
					ts_data = res[0]
					ts_data["band"] = self.sourceToken.band
					ts_data["ssa_location"] = pgsphere.SPoint.fromDegrees(
						ts_data["ra"], ts_data["dec"])
					return ts_data
				</code>
			</pargetter>
		</embeddedGrammar>

		<make table="instance_G">
			<parmaker idmaps="ra,dec,source_id,ssa_location">
				<map key="title">("Gaia DR2 %(band)s photometry time series for"
					" star %(source_id)s")%vars</map>
			</parmaker>
			<rowmaker idmaps="*"/>
		</make>
	</data>

	<service id="tsdl" allowed="dlmeta,dlget">
		<meta name="title">Gaia DR2 time series datalink</meta>
		<meta name="description">This service gives photometric
			time series for the roughly 500000 objects for which Gaia DR2
			gives epoch photometry.  Note that this is different from
			the ESAC datalink service in that it gives split time series
			for the three Gaia bands.  There is a link to the original
			ESAC time series in our table, though.  The ids passed to
			this service are Gaia DR2 source ids.
		</meta>

		<datalinkCore>
			<descriptorGenerator>
				<setup>
					<code>
						from gavo import svcs

						class TSDescriptor(ProductDescriptor):
							def __init__(self, pubDID):
								# accept both naked sourceIds and proper pubDIDs here
								# pubDIDs can end with source_id (all bands)
								# and source_id/bandpass (just the referenced band)
								try:
									if pubDID.startswith("\pubDIDBase"):
										self.pubDID = pubDID
										parts = pubDID.split("/")
										if parts[-1] in ["BP", "RP", "G"]:
											self.sourceId = int(parts[-2])
											self.metaBandpass = parts[-1]
										else:
											self.sourceId = int(parts[-1])
											self.metaBandpass = None

									else:
										self.pubDID = "\pubDIDBase%s"%pubDID
										self.sourceId = int(pubDID)
										self.metaBandpass = None

								except ValueError:
									raise svcs.UnknownURI(
										"%s does not look like a Gaia DR2 publisher DID"
										" from us."%pubDID)

								self.suppressAutoLinks = True

								with base.getTableConn() as conn:
									res = list(conn.queryToDicts(
										"SELECT * FROM \schema.dr2epochflux"
										" WHERE source_id=%(source_id)s",
										{"source_id": int(self.sourceId)}))
								if not res:
									raise svcs.UnknownURI(
										"Gaia DR2 has no epoch photometry for %s"%self.sourceId)

								self.row = res[0]
					</code>
				</setup>
				<code>
					return TSDescriptor(pubDID)
				</code>
			</descriptorGenerator>

			<metaMaker semantics="#progenitor">
				<code>
					if descriptor.pubDID is None:
						# We're generating meta for an in-SSA-response block
						# and can't link to the original ESA data in this way.
						return

					yield descriptor.makeLink(
						"http://geadata.esac.esa.int/data-server/data?"
							"RETRIEVAL_TYPE=epoch_photometry&amp;ID=%s"%descriptor.sourceId,
						description="Merged-band custom time series from ESAC",
						contentType="application/x-votable+xml",
						contentLength=15000)
				</code>
			</metaMaker>

			<metaMaker semantics="#coderived">
				<code>
					if descriptor.pubDID is None:
						# We're generating meta for an in-SSA-response block
						# and can't vary band in this way.
						return
					for band in ["G", "BP", "RP"]:
						yield descriptor.makeLink(
							makeAbsoluteURL("\rdId/tsdl/dlget?ID=%s&amp;BANDPASS=%s"%(
								descriptor.pubDID, band)),
							description="%s time series for Gaia DR2 %s."%(
								band, descriptor.sourceId),
							semantics="#this"
								if descriptor.metaBandpass is None
									or descriptor.metaBandpass==band
								else "#coderived",
							contentType="application/x-votable+xml",
							contentLength=15000,
							contentQualifier="#timeseries")
				</code>
			</metaMaker>

			<metaMaker>
				<code>
					# interpreted by the first data function
					yield MS(InputKey, name="BANDPASS", type="text",
						description="Gaia bandpass to generate the time series for.",
						required=True, multiplicity="single",
						values=MS(Values, options=[MS(Option, content_=b)
							for b in ["G", "BP", "RP"]]))
				</code>
			</metaMaker>

			<dataFunction>
				<setup>
					<code>
						from gavo import rsc
					</code>
				</setup>
				<code>
					descriptor.band = args["BANDPASS"]

					dd = rd.getById("make_instance")
					descriptor.data = rsc.Data.createWithTable(dd,
						rd.getById("instance_"+descriptor.band))
					descriptor.data = rsc.makeData(
						dd,
						data=descriptor.data,
						forceSource=descriptor)
				</code>
			</dataFunction>

			<dataFormatter>
				<code>
					from gavo import formats
					return (base.votableType, 	
						formats.getFormatted("vodml", descriptor.data))
				</code>
			</dataFormatter>
		</datalinkCore>
	</service>

	<table id="dr2_ts_ssa" onDisk="true" adql="True">
		<meta name="title">Gaia DR2 Timeseries SSA Table</meta>
		<meta name="description">
			This table contains about 1.5 Million photometric timeseries
			for roughly 0.5 Million objects.  Photometry is available in the Gaia
			G, BP, and RP bands for epochs between 2014-07-25 and 2016-05-25.
			The spectra are available in VOTable format with the timeseries
			annotation proposed in the Nadvornik et al IVOA note.
			</meta>

		<FEED source="//scs#splitPosIndex"
			long="degrees(long(ssa_location))" lat="degrees(lat(ssa_location))"/>
		<index columns="time_min"/>
		<index columns="time_max"/>
		<index columns="ssa_specstart"/>
		<index columns="ssa_specend"/>

		<meta name="_associatedDatalinkService">
			<meta name="serviceId">tsdl</meta>
			<meta name="idColumn">ssa_pubDID</meta>
		</meta>

		<mixin
			fluxUCD="phot.flux.density;em.wl"
			fluxUnit="s**-1"
			spectralUnit="m"
			spectralUCD="em.wl"
			>//ssap#mixc</mixin>

		<mixin
			calibLevel="2"
			tMax="time_max"
			tMin="time_min"
			tResolution="1e5"
			createDIDIndex="True"
			instrument_name="'Gaia'"
			>//obscore#publishSSAPMIXC</mixin>
		
		<stc>
			TimeInterval TCB BARYCENTER "time_min" "time_max"
		</stc>

		<column name="time_min" type="double precision"
			unit="d" ucd="time.epoch;stat.min"
			tablehead="1. Obs"
			description="First timestamp in time series (MJD Barycentric TCB)"
			verbLevel="1"/>
		<column name="time_max" type="double precision"
			unit="d" ucd="time.epoch;stat.max"
			tablehead="Last Obs"
			description="Last timestamp in time series (MJD Barycentric TCB)"
			verbLevel="1"/>

	</table>

	<data id="import_ssa">
		<sources>
			<item>
				SELECT *
				FROM gaia.dr2light
				JOIN gaia.dr2epochflux
				USING (source_id)
			</item>
		</sources>

		<embeddedGrammar>
			<iterator>
				<code>
					with base.getTableConn() as conn:
						for row in conn.queryToDicts(self.sourceToken):
							yield row
				</code>
			</iterator>
			<rowfilter name="dispatch_bands">
				<code>
					@accrefBase = makeAbsoluteURL("\rdId/tsdl/dlget?ID=%s&amp;"%(
						@source_id))

					@bandpass = "Gaia G"
					@times = @g_transit_time
					@bandpass_lo = 325e-9
					@bandpass_mid = 600e-9
					@bandpass_hi = 1000e-9
					yield row

					@bandpass = "Gaia BP"
					@times = @bp_obs_time
					@bandpass_lo = 325e-9
					@bandpass_mid = 500e-9
					@bandpass_hi = 670e-9
					yield row

					@bandpass = "Gaia RP"
					@times = @rp_obs_time
					@bandpass_lo = 625e-9
					@bandpass_mid = 750e-9
					@bandpass_hi = 1000e-9
					yield row
				</code>
			</rowfilter>

			<rowfilter procDef="//products#define">
				<bind name="accref">"\rdId/%s/%s"%(
					@source_id, @bandpass.split()[-1])</bind>
				<bind name="path">@accrefBase+"BANDPASS="+@bandpass.split()[-1]</bind>
				<bind name="fsize">10000</bind>
				<bind name="mime">"application/x-votable+xml"</bind>
				<bind name="table">"\schema.dr2epochflux"</bind>
				<bind name="preview">makeAbsoluteURL("\rdId/preview/qp/%s/%s"%(
          @bandpass.split()[-1], @source_id))</bind>
				<bind name="preview_mime">"image/png"</bind>
			</rowfilter>
		</embeddedGrammar>

		<make table="dr2_ts_ssa">
			<rowmaker idmaps="*">
				<apply name="compute_time_span">
					<code>
						if @times is None:
							raise IgnoreThisRow()
						@times = [v for v in @times if v==v]
						@time_min = min(@times)+2455197.5-stc.JD_MJD
						@time_max = max(@times)+2455197.5-stc.JD_MJD
					</code>
				</apply>

				<apply procDef="//ssap#setMeta">
					<bind name="alpha">@ra</bind>
					<bind name="bandpass">@bandpass</bind>
					<bind name="cdate">datetime.datetime(2018, 4, 25)</bind>
					<bind name="dateObs">(@time_max+@time_min)/2</bind>
					<bind name="delta">@dec</bind>
					<bind key="dstitle">"Gaia DR2 %s lightcurve for %s"%(
						@bandpass, @source_id)</bind>
					<bind name="length">len(@times)</bind>
					<bind name="pubDID">\standardPubDID</bind>
					<bind name="specstart">@bandpass_lo</bind>
					<bind name="specend">@bandpass_hi</bind>
					<bind name="targclass">"V*"</bind>
					<bind name="targname">"Gaia DR2 %s"%@source_id</bind>
					<bind name="timeExt">@time_max-@time_min</bind>
				</apply>

				<apply procDef="//ssap#setMixcMeta">
					<bind name="binSize">5e-7</bind>
					<bind name="collection"
						>"Gaia DR2"</bind>
					<bind name="creationType">"archival"</bind>
					<bind name="dataSource">"survey"</bind>
					<bind name="dstype">"timeseries"</bind>
					<bind name="reference">"2018gdr2.reptE...5B"</bind>
					<bind name="fluxCalib">"ABSOLUTE"</bind>
					<bind name="specCalib">"ABSOLUTE"</bind>
				</apply>
			</rowmaker>
		</make>
	</data>

	<service id="ssa" allowed="form,ssap.xml">
		 <meta name="shortName">GDR2 TS SSAP</meta>
		<meta name="ssap.dataSource">survey</meta>
		<meta name="ssap.creationType">archival</meta>
		<meta name="ssap.testQuery">MAXREC=1</meta>
		<meta name="ssap.complianceLevel">query</meta>
		<meta name="productTypesServed">timeseries</meta>

		<publish render="ssap.xml" sets="ivo_managed"/>
		<meta name="title">Gaia DR2 light curves SSA</meta>
		<meta name="description">This service exposes about 0.5 million
			light curves of stars classified as variable by the Gaia analysis
			system through the VO SSAP protocol.  The lightcurves are
			published per-band and are also available through obscore.</meta>

		<ssapCore queriedTable="dr2_ts_ssa">
			<FEED source="//ssap#hcd_condDescs">
				<PRUNE id="timeCond"/>
			</FEED>
			<condDesc>
				<inputKey name="TIME" type="pql-float"
					unit="d" ucd="time.epoch"
					utype="ssa:Char.TimeAxis.Coverage.Location.Value"
					description="Time covered by the time series"
					multiplicity="forced-single">
					<values min="56863" max="57532"/>
				</inputKey>
				<phraseMaker>
					<code>
					parsed = pql.PQLFloatPar.fromLiteral(
						inPars.get("TIME", None), "TIME")
					yield parsed.getSQLForInterval(
						"time_min", "time_max", outPars)
					</code>
				</phraseMaker>
			</condDesc>
		</ssapCore>
	</service>

	<service id="dr2lcone" allowed="form,scs.xml">
		 <meta name="shortName">GDR2light SCS</meta>
		<publish render="scs.xml" sets="ivo_managed"/>
		<meta name="title">Gaia DR2-light Cone Search</meta>
		<meta>
			testQuery.ra:  312.76222908
			testQuery.dec:  6.690190
			testQuery.sr:  0.0001
		</meta>
		<scsCore queriedTable="dr2light">
			<FEED source="//scs#coreDescs"/>
			<LOOP listItems="parallax phot_g_mean_mag phot_bp_mean_mag
				phot_rp_mean_mag">
		<events>
			<condDesc buildFrom="\item"/>
		</events>
	</LOOP>
		</scsCore>
	</service>

	<service id="preview" allowed="qp">
		<meta name="title">Gaia DR2 epoch photometry preview maker"</meta>
		<property name="queryField">spec_id</property>
		<pythonCore>
			<inputTable>
				<inputKey name="spec_id" type="text" required="True"
					description="ID of the time series to produce a
						preview for, in the forum band/source_id."/>
			</inputTable>
			<coreProc>
				<setup imports="gavo.helpers.processing.SpectralPreviewMaker,
					gavo.svcs"/>
				<code>
					band, sourceId = inputTable.args["spec_id"].split("/")
					timeCol, fluxCol = {
						"G": ("g_transit_time", "g_transit_flux"),
						"RP": ("rp_obs_time", "rp_flux"),
						"BP": ("bp_obs_time", "bp_flux"),}[band]
					sourceId = int(sourceId)

					with base.getTableConn() as conn:
						res = list(conn.query(f"SELECT {timeCol}, {fluxCol}"
							" FROM \schema.dr2epochflux"
							" WHERE source_id=%(sourceId)s",
							locals()))

					if not res:
						raise svcs.UnknownURI("No such spectrum known here")

					return ("image/png", SpectralPreviewMaker.get2DPlot(
						list(zip(res[0][0], res[0][1])), linear=True))
				</code>
			</coreProc>
		</pythonCore>
	</service>

	<!--  #################### end epoch photometry -->

	<regSuite title="GDR2 regression">
		<regTest title="GDR2 table present">
			<url parSet="TAP"
				QUERY="SELECT * FROM gaia.dr2light
					WHERE source_id=1737546272482386432">/tap/sync</url>
			<code>
				row = self.getFirstVOTableRow()
				self.assertEqual(len(row), 27)
				self.assertEqual(row["radial_velocity"], None)
				self.assertAlmostEqual(row["ra"], 312.866560795216)
				self.assertAlmostEqual(row["ruwe"], 1.0473799705505, places=6)
			</code>
		</regTest>

		<regTest title="GDR2 epoch photometry works">
			<url parSet="TAP"
				QUERY="SELECT * FROM gaia.dr2epochflux
					WHERE source_id=1737550807967929472">/tap/sync</url>
			<code>
				row = self.getFirstVOTableRow()
				self.assertEqual(len(row), 12)
				self.assertEqual(len(row["bp_obs_time"]), len(row["bp_flux"]))
				self.assertEqual(len(row["bp_obs_time"]), len(row["bp_flux_error"]))

				# if the following is larger than 47, NaN-removal is broken
				self.assertEqual(len(row["bp_obs_time"]), 47)
				self.assertEqual(len(row["rp_obs_time"]), 48)

				self.assertEqual(len(row["rp_obs_time"]), len(row["rp_flux"]))
				self.assertEqual(len(row["rp_obs_time"]), len(row["rp_flux_error"]))

				self.assertEqual(len(row["g_transit_time"]),
					len(row["g_transit_flux"]))
				self.assertEqual(len(row["g_transit_flux"]),
					len(row["g_transit_flux_error"]))

				self.assertEqual(row["rp_obs_time"][0], 1761.3505379502)

				for val in row["bp_obs_time"]+row["rp_obs_time"]+row["g_transit_time"]:
					assert(val==val)
			</code>
		</regTest>

		<regTest title="GDR2 timeseries datalink meta looks right">
			<url ID="ivo://org.gavo.dc/~?gaia/q2/1737549120045621120"
				>tsdl/dlmeta</url>
			<code>
				bySemantics = {}
				for row in self.getVOTableRows():
					bySemantics.setdefault(row["semantics"], []).append(row)
				
				self.assertEqual([
					r["access_url"].split("q2/tsdl/")[-1]
						for r in bySemantics["#this"]], [
					"dlget?ID=ivo://org.gavo.dc/~?gaia/q2/1737549120045621120&amp;BANDPASS=G",
					"dlget?ID=ivo://org.gavo.dc/~?gaia/q2/1737549120045621120&amp;BANDPASS=BP",
					"dlget?ID=ivo://org.gavo.dc/~?gaia/q2/1737549120045621120&amp;BANDPASS=RP"])
				self.assertEqual(
					bySemantics["#this"][0]["content_type"],
					"application/x-votable+xml")

				self.assertEqual(
					set(r["content_qualifier"] for r in bySemantics["#this"]),
					{'#timeseries'})
			</code>
		</regTest>

		<regTest title="GDR2 timeseries datalink meta accepts identifier
				with band">
			<url ID="ivo://org.gavo.dc/~?gaia/q2/1737549120045621120/BP"
				>tsdl/dlmeta</url>
			<code>
				bySemantics = {}
				for row in self.getVOTableRows():
					bySemantics.setdefault(row["semantics"], []).append(row)
				
				self.assertEqual([
					r["access_url"].split("q2/tsdl/")[-1]
						for r in bySemantics["#this"]], [
					"dlget?ID=ivo://org.gavo.dc/~?gaia/q2/1737549120045621120/BP&amp;BANDPASS=BP"])
				self.assertEqual([
					r["access_url"].split("q2/tsdl/")[-1]
						for r in bySemantics["#coderived"]], [
					"dlget?ID=ivo://org.gavo.dc/~?gaia/q2/1737549120045621120/BP&amp;BANDPASS=G",
					"dlget?ID=ivo://org.gavo.dc/~?gaia/q2/1737549120045621120/BP&amp;BANDPASS=RP"])
			</code>
		</regTest>

		<regTest title="GDR2 per-band time series produced (and bad points
				swallowed)">
			<url BANDPASS="BP" ID="1737549120045621120">tsdl/dlget</url>
			<code>
				self.assertXpath("//m:INSTANCE[@dmtype='votable:SpaceFrame']/m:ATTRIBUTE[@dmrole='epoch']",
					 {"value": "J2015.5", "dmtype": "ivoa:string"})
				self.assertXpath(
					"//v:PARAM[@ID=//m:ATTRIBUTE[@dmrole='longitude']/@ref]",
					{"value": "312.995282218611", "ucd": "pos.eq.ra"})
				self.assertXpath("//v:TR[1]/v:TD[1]", {
					None: "2456958.850349069"})
				self.assertXpath("//v:FIELD[@name='flux']",
					{"ucd": "phot.flux;em.opt.B"})
				self.assertFalse(b"NaN" in self.data)
			</code>
		</regTest>

		<regTest title="GDR2 dlget accepts full pubDIDs, too, and has mags">
			<url BANDPASS="RP"
				ID="ivo://org.gavo.dc/~?gaia/q2/1737549120045621120/G">tsdl/dlget</url>
			<code>
				self.assertXpath("//m:VODML/m:TEMPLATES/m:INSTANCE["
					"@dmtype='votable:Coords'][1]//m:ATTRIBUTE[@dmrole='epoch']/@value",
					{None: "J2015.5"})
				self.assertXpath(
					"//v:PARAM[@ID=//m:ATTRIBUTE[@dmrole='longitude']/@ref]",
					{"value": "312.995282218611", "ucd": "pos.eq.ra"})
				self.assertXpath("//v:FIELD[@name='flux']",
					{"ucd": "phot.flux;em.opt.R"})
				self.assertXpath("//v:FIELD[@name='phot']",
					{"ucd": "phot.mag;em.opt.R"})
				self.assertXpath("//v:TR[1]/v:TD[1]", {
					None: "2456958.850435664"})
				self.assertXpath("//v:TR[1]/v:TD[2]", {
					None: EqualingRE(r"16.9429\\d*")})
			</code>
		</regTest>

		<regTest title="GDR2 SSA yields expected results">
			<url REQUEST="queryData" POS="314.31,7.6411" SIZE="0.1"
				TIME="57510.713/58000">ssa/ssap.xml</url>
			<code>
				# The TIME condition should filter out the G time series (which
				# starts a bit earlier).
				rows = self.getVOTableRows()
				self.assertEqual(len(rows), 2) # if 3, then TIME matching is broken
				self.assertEqual(rows[0]["ssa_targname"],
					'Gaia DR2 1737539915930483712')
				self.assertEqual(rows[0]["ssa_length"], 21)
			</code>
		</regTest>
	</regSuite>
</resource>
