diff --git a/CEP/Calibration/CMakeLists.txt b/CEP/Calibration/CMakeLists.txt
index a3c04936e25cd3d4d3bfe04905c4e4733f06b4f2..c957fbcc8dabae852e589ded2558bd9148b4d4cc 100644
--- a/CEP/Calibration/CMakeLists.txt
+++ b/CEP/Calibration/CMakeLists.txt
@@ -2,3 +2,7 @@
 
 lofar_add_package(BBSKernel)
 lofar_add_package(BBSControl)
+lofar_add_package(ExpIon)
+lofar_add_package(pystationresponse)
+lofar_add_package(ElementResponse)
+lofar_add_package(StationResponse)
diff --git a/CEP/Calibration/ElementResponse/CMakeLists.txt b/CEP/Calibration/ElementResponse/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a5bc6b53164ea50c6088698dec00a2c047517a0b
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/CMakeLists.txt
@@ -0,0 +1,10 @@
+# $Id$
+
+lofar_package(ElementResponse 0.1 DEPENDS Common)
+
+# Uncomment to check for unsafe conversions (gcc), for example conversion of
+# size_t to unsigned int (truncation).
+#add_definitions(-Wconversion)
+
+add_subdirectory(include/ElementResponse)
+add_subdirectory(src)
diff --git a/CEP/Calibration/ElementResponse/include/ElementResponse/CMakeLists.txt b/CEP/Calibration/ElementResponse/include/ElementResponse/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a97059ef3720c45a058d22dad5e7024dda6488ac
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/include/ElementResponse/CMakeLists.txt
@@ -0,0 +1,11 @@
+# $Id$
+
+# Create symbolic link to include directory.
+execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_BINARY_DIR}/include/${PACKAGE_NAME})
+
+# Install header files.
+install(FILES
+  ElementResponse.h
+  DESTINATION include/${PACKAGE_NAME})
diff --git a/CEP/Calibration/ElementResponse/include/ElementResponse/ElementResponse.h b/CEP/Calibration/ElementResponse/include/ElementResponse/ElementResponse.h
new file mode 100644
index 0000000000000000000000000000000000000000..f95ba672544e94f30f6046d752f3cf73ffa1c853
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/include/ElementResponse/ElementResponse.h
@@ -0,0 +1,101 @@
+//# ElementResponse.h: Functions to compute the (idealized) response of a LOFAR
+//# LBA or HBA dual dipole antenna.
+//#
+//# Copyright (C) 2011
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_ELEMENTRESPONSE_H
+#define LOFAR_ELEMENTRESPONSE_H
+
+// \file
+// Functions to compute the (idealized) response of a LOFAR LBA or HBA dual
+// dipole antenna.
+
+#include <complex>
+
+namespace LOFAR
+{
+
+// \addtogroup ElementResponse
+// @{
+
+// Compute the response of an idealized LOFAR LBA dual dipole antenna to
+// radiation at frequency freq (Hz) arriving from the direction given by theta,
+// phi (rad). The antenna model is described in a spherical coordinate system
+// with coordinates theta (zenith angle) and phi (azimith). The +X dipole is at
+// azimuth zero, the +Y dipole is at azimuth PI / 2.0.
+//
+// Preconditions:
+// --------------
+// freq: Frequency in Hz in the range [10 MHz, 100 MHz].
+// theta: Zenith angle in rad in the range [0.0, PI / 2.0].
+// phi: Azimuth in rad in the range [0.0, 2.0 * PI].
+//
+void element_response_lba(double freq, double theta, double phi,
+    std::complex<double> (&response)[2][2]);
+
+// Compute the response of an idealized LOFAR HBA dual dipole antenna to
+// radiation at frequency freq (Hz) arriving from the direction given by theta,
+// phi (rad). The antenna model is described in a spherical coordinate system
+// with coordinates theta (zenith angle) and phi (azimith). The +X dipole is at
+// azimuth zero, the +Y dipole is at azimuth PI / 2.0.
+//
+// Preconditions:
+// --------------
+// freq: Frequency in Hz in the range [120 MHz, 240 MHz].
+// theta: Zenith angle in rad in the range [0.0, PI / 2.0].
+// phi: Azimuth in rad in the range [0.0, 2.0 * PI].
+//
+void element_response_hba(double freq, double theta, double phi,
+    std::complex<double> (&response)[2][2]);
+
+// Compute the response of an idealized LOFAR dual dipole antenna to radiation
+// at frequency freq (Hz) arriving from the direction given by theta, phi (rad).
+// The antenna model is described in a spherical coordinate system with
+// coordinates theta (zenith angle) and phi (azimith). The +X dipole is at
+// azimuth zero, the +Y dipole is at azimuth PI / 2.0.
+//
+// This function uses a set of user defined coefficients to evaluate the beam
+// model. The coeff_shape parameter defines the shape of the coefficient array
+// as no. of harmonics x degree in theta x degree in frequency x 2. The last
+// dimension is implicit and always equal to 2. The coeff parameter points to an
+// array of coefficients of the proper size, stored in row-major order
+// ("C"-order). The freq_center and freq_range parameters define the frequency
+// range covered by the model described by the set of coefficients.
+//
+// Preconditions:
+// --------------
+// freq: Frequency in Hz in the range [freq_center - freq_range,
+//     freq_center + freq_range].
+// theta: Zenith angle in rad in the range [0.0, PI / 2.0].
+// phi: Azimuth in rad in the range [0.0, 2.0 * PI].
+// freq_range, freq_center: Frequency center and range in Hz, should be > 0.
+// coeff_shape: Shape of the coefficient array, all dimensions should be > 0.
+//
+void element_response(double freq, double theta, double phi,
+    std::complex<double> (&response)[2][2], double freq_center,
+    double freq_range, const unsigned int (&coeff_shape)[3],
+    const std::complex<double> coeff[]);
+
+// @}
+
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/ElementResponse/src/CMakeLists.txt b/CEP/Calibration/ElementResponse/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fbbf6cf0cc590054491f91aa4ef16c2b2732ccf5
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/CMakeLists.txt
@@ -0,0 +1,7 @@
+# $Id: CMakeLists.txt 18775 2011-09-06 13:36:45Z zwieten $
+
+include(LofarPackageVersion)
+
+lofar_add_library(elementresponse
+  Package__Version.cc
+  ElementResponse.cc)
diff --git a/CEP/Calibration/ElementResponse/src/DefaultCoeffHBA.cc b/CEP/Calibration/ElementResponse/src/DefaultCoeffHBA.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c37da7e5e27a609311dcc132013ba358ab9d3eb7
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/DefaultCoeffHBA.cc
@@ -0,0 +1,74 @@
+// Beam model coefficients converted by convert_coeff.py.
+// Conversion performed on 2011/09/14/08:23:16 UTC using: 
+//     convert_coeff.py element_beam_HBA.coeff DefaultCoeffHBA.cc default_hba
+
+#include <complex>
+
+// Center frequency, and frequency range for which the beam model coefficients
+// are valid. The beam model is parameterized in terms of a normalized
+// frequency f' in the range [-1.0, 1.0]. The appropriate conversion is:
+//
+//     f' = (f - center) / range
+//
+const double default_hba_freq_center = 180e6;
+const double default_hba_freq_range = 60e6;
+
+// Shape of the coefficient array: 2x5x5x2 (the size of the last dimension is
+// implied, and always equal to 2).
+//
+const unsigned int default_hba_coeff_shape[3] = {2, 5, 5};
+
+// The array of coefficients in row-major order ("C"-order).
+//
+const std::complex<double> default_hba_coeff[100] = {
+    std::complex<double>(0.9989322499459223, 0.0003305895124867), std::complex<double>(1.0030546028872600, 0.0002157249025076),
+    std::complex<double>(0.0003002209532403, 0.0007909077657054), std::complex<double>(0.0022051270911392, 0.0003834815341981),
+    std::complex<double>(-0.0003856663268042, 0.0008435910525861), std::complex<double>(0.0004887765294093, 0.0002777796480946),
+    std::complex<double>(-0.0000699366665322, 0.0005136144371953), std::complex<double>(0.0001520602842105, 0.0001303481681886),
+    std::complex<double>(0.0000512381993616, 0.0001550785137302), std::complex<double>(0.0000819244737818, 0.0000466470412396),
+    std::complex<double>(0.0249658150445263, -0.0122024663463393), std::complex<double>(-0.0917825091832822, -0.0062606338208358),
+    std::complex<double>(-0.0083709499453879, -0.0289759752488368), std::complex<double>(-0.0689260153643395, -0.0111348626546314),
+    std::complex<double>(0.0116296166994115, -0.0307342946951178), std::complex<double>(-0.0171249717275797, -0.0080642275561593),
+    std::complex<double>(0.0012408055399100, -0.0191295543986957), std::complex<double>(-0.0051031652662961, -0.0037143632875100),
+    std::complex<double>(-0.0022414352263751, -0.0060474723525871), std::complex<double>(-0.0024377933436567, -0.0012852163337395),
+    std::complex<double>(-0.6730977722052307, 0.0940030437973656), std::complex<double>(0.3711597596859299, 0.0557089394867947),
+    std::complex<double>(0.2119250520015808, 0.2155514942677135), std::complex<double>(0.6727380529527980, 0.0989550572104158),
+    std::complex<double>(-0.0419944347289523, 0.2355624543349744), std::complex<double>(0.1917656461134636, 0.0732470381581913),
+    std::complex<double>(0.0048918921441903, 0.1588912409502319), std::complex<double>(0.0575369727210951, 0.0344677222786687),
+    std::complex<double>(0.0241014578366618, 0.0547046570516960), std::complex<double>(0.0219986510834463, 0.0112189146988984),
+    std::complex<double>(0.0665319393516388, -0.1418009730472832), std::complex<double>(-0.7576728614553603, -0.0472040122949963),
+    std::complex<double>(-0.1017024786435272, -0.3302620837788515), std::complex<double>(-0.5600906156274197, -0.0797555201430585),
+    std::complex<double>(0.0889729243872774, -0.3406964719938829), std::complex<double>(-0.1342560801672904, -0.0515926960946038),
+    std::complex<double>(-0.0149335262655201, -0.2084962323582034), std::complex<double>(-0.0327252678958813, -0.0172371907472848),
+    std::complex<double>(-0.0362395089905272, -0.0661322227928722), std::complex<double>(-0.0141568558526096, -0.0042676979206835),
+    std::complex<double>(0.1121669548152054, 0.0504713119323919), std::complex<double>(0.1882531376700409, 0.0088411256350159),
+    std::complex<double>(0.0066968933526899, 0.1181452711088882), std::complex<double>(0.0981630367567397, 0.0129921405004959),
+    std::complex<double>(-0.0347327225501659, 0.1186585563636635), std::complex<double>(0.0102831315790362, 0.0046275244914932),
+    std::complex<double>(0.0070209144233666, 0.0689639468490938), std::complex<double>(-0.0020239346031291, -0.0025499069613344),
+    std::complex<double>(0.0132702874173192, 0.0207916487187541), std::complex<double>(0.0004387107229914, -0.0017223838914815),
+    std::complex<double>(-0.0004916757488397, 0.0000266213616248), std::complex<double>(0.0006516553273188, -0.0000433166563288),
+    std::complex<double>(-0.0004357897643121, 0.0000320567996700), std::complex<double>(0.0005818285824826, -0.0001021069650381),
+    std::complex<double>(-0.0001047488648808, -0.0000302146563592), std::complex<double>(0.0001593350153828, -0.0000879125663990),
+    std::complex<double>(-0.0000141882506567, -0.0000941521783975), std::complex<double>(-0.0000004226298134, -0.0000245060763932),
+    std::complex<double>(-0.0000177429496833, -0.0000561890408003), std::complex<double>(-0.0000018388829279, 0.0000032387726477),
+    std::complex<double>(0.0162495046881796, -0.0010736997976255), std::complex<double>(-0.0175635905033026, 0.0012997068962173),
+    std::complex<double>(0.0138897851110661, -0.0014876219938565), std::complex<double>(-0.0150211436594772, 0.0029712291209158),
+    std::complex<double>(0.0031705620225488, 0.0004838463688512), std::complex<double>(-0.0034418973689263, 0.0024603729467258),
+    std::complex<double>(0.0003028387544878, 0.0026905629457281), std::complex<double>(0.0006768121359769, 0.0005901486396051),
+    std::complex<double>(0.0004634797107989, 0.0016976603895716), std::complex<double>(0.0003344773954073, -0.0001499932789294),
+    std::complex<double>(-0.1492097398080444, 0.0123735410547393), std::complex<double>(0.1393121453502456, -0.0121117146246749),
+    std::complex<double>(-0.1217628319418324, 0.0222643129255504), std::complex<double>(0.1108579917761457, -0.0262986164183475),
+    std::complex<double>(-0.0273147374272124, 0.0098595182007132), std::complex<double>(0.0208992817013466, -0.0205929453727953),
+    std::complex<double>(-0.0002152227668601, -0.0089220757225133), std::complex<double>(-0.0074792188817697, -0.0043562231368076),
+    std::complex<double>(-0.0012019994038721, -0.0079939660050373), std::complex<double>(-0.0035807498769946, 0.0014801422733613),
+    std::complex<double>(0.1567990061437258, -0.0143275575385193), std::complex<double>(-0.1043118778001582, 0.0106756004832779),
+    std::complex<double>(0.1151024257152241, -0.0225518489392044), std::complex<double>(-0.0593437249231851, 0.0216080058910987),
+    std::complex<double>(0.0142781186223020, -0.0057037138045721), std::complex<double>(0.0151043140114779, 0.0141435752121475),
+    std::complex<double>(-0.0057143555179676, 0.0141142700941743), std::complex<double>(0.0251435557201315, -0.0005753615445942),
+    std::complex<double>(0.0004475745352473, 0.0102135659618127), std::complex<double>(0.0090474375150397, -0.0032177128650026),
+    std::complex<double>(-0.0459124372023251, 0.0044990718645418), std::complex<double>(0.0135433541303599, -0.0021789296923529),
+    std::complex<double>(-0.0306136798186735, 0.0064963361606382), std::complex<double>(-0.0046440676338940, -0.0037281688158807),
+    std::complex<double>(-0.0006372791846825, 0.0008894047150233), std::complex<double>(-0.0181611528840412, -0.0011106177431486),
+    std::complex<double>(0.0032325387394458, -0.0048123509184894), std::complex<double>(-0.0136340313457176, 0.0021185000810664),
+    std::complex<double>(0.0001287985092565, -0.0032079544559908), std::complex<double>(-0.0045503800737417, 0.0015366231416036)
+};
diff --git a/CEP/Calibration/ElementResponse/src/DefaultCoeffLBA.cc b/CEP/Calibration/ElementResponse/src/DefaultCoeffLBA.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d196d2356d16a71b0f153b5364ac153bb5829f81
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/DefaultCoeffLBA.cc
@@ -0,0 +1,74 @@
+// Beam model coefficients converted by convert_coeff.py.
+// Conversion performed on 2011/09/14/08:23:09 UTC using: 
+//     convert_coeff.py element_beam_LBA.coeff DefaultCoeffLBA.cc default_lba
+
+#include <complex>
+
+// Center frequency, and frequency range for which the beam model coefficients
+// are valid. The beam model is parameterized in terms of a normalized
+// frequency f' in the range [-1.0, 1.0]. The appropriate conversion is:
+//
+//     f' = (f - center) / range
+//
+const double default_lba_freq_center = 55e6;
+const double default_lba_freq_range = 45e6;
+
+// Shape of the coefficient array: 2x5x5x2 (the size of the last dimension is
+// implied, and always equal to 2).
+//
+const unsigned int default_lba_coeff_shape[3] = {2, 5, 5};
+
+// The array of coefficients in row-major order ("C"-order).
+//
+const std::complex<double> default_lba_coeff[100] = {
+    std::complex<double>(0.9982445079290715, 0.0000650863154389), std::complex<double>(1.0006230902158257, -0.0022053287681416),
+    std::complex<double>(0.0001002692200362, 0.0006838211278268), std::complex<double>(-0.0003660049052840, -0.0008418920419220),
+    std::complex<double>(-0.0010581424498791, 0.0015237878543047), std::complex<double>(0.0007398729642721, 0.0028468649470433),
+    std::complex<double>(-0.0039458389254656, -0.0007048354913730), std::complex<double>(0.0007040177887611, 0.0007856369612188),
+    std::complex<double>(-0.0031701591723043, -0.0010521154166512), std::complex<double>(-0.0007213036752903, -0.0007227764008022),
+    std::complex<double>(0.0550606068782634, 0.0011958385659938), std::complex<double>(-0.0160912944232080, 0.0703645376267940),
+    std::complex<double>(0.0033849565901213, -0.0244636379385135), std::complex<double>(0.0234264238829944, 0.0084068836453700),
+    std::complex<double>(0.0557107413978542, -0.0634701730653090), std::complex<double>(-0.0139549526991330, -0.1175401658864208),
+    std::complex<double>(0.1336911750356096, 0.0202651327657687), std::complex<double>(-0.0113385668361727, -0.0339262369086247),
+    std::complex<double>(0.0962263571740972, 0.0440074333288440), std::complex<double>(0.0313595045238824, 0.0230763038515351),
+    std::complex<double>(-0.8624889445327827, -0.1522883072804402), std::complex<double>(-0.0386800869486029, -0.7569350701887934),
+    std::complex<double>(0.0891332399420108, 0.1876527151756476), std::complex<double>(-0.1012363483900640, -0.1975118891151966),
+    std::complex<double>(-0.6404795825927633, 0.7568775384981410), std::complex<double>(0.0767245154665722, 1.3441875993523555),
+    std::complex<double>(-0.8758406699506004, 0.3350237639226141), std::complex<double>(0.2824832769101577, 0.6821307442669313),
+    std::complex<double>(-0.3144282315609649, -0.2763869580286276), std::complex<double>(-0.1705959031354030, -0.0712085950559831),
+    std::complex<double>(0.4039567648146965, 0.0810473144253429), std::complex<double>(-0.0350803390479135, 0.5214591717801087),
+    std::complex<double>(0.2232030356124932, -0.2248154851829713), std::complex<double>(0.4704343293662089, -0.3552101485419532),
+    std::complex<double>(0.9646419509627557, -0.8095088593139815), std::complex<double>(0.1635280638865702, -1.4854352979459096),
+    std::complex<double>(1.0331569921006993, 0.0509705885336283), std::complex<double>(0.1501121326521990, -0.5193414816770609),
+    std::complex<double>(0.4715775965513117, 0.5077361528286819), std::complex<double>(0.3847391427972284, 0.1136717951238837),
+    std::complex<double>(-0.0756250564248881, 0.0056622911723172), std::complex<double>(-0.1267444401630109, -0.0349676272376008),
+    std::complex<double>(-0.1793752883639813, 0.0720222655359702), std::complex<double>(-0.2678542619793421, 0.3152115802895427),
+    std::complex<double>(-0.3718069213271066, 0.2275266747872172), std::complex<double>(-0.1372223722572021, 0.4314989948093362),
+    std::complex<double>(-0.3316657641578328, -0.1655909947939444), std::complex<double>(-0.2158100484836540, 0.0614504774034524),
+    std::complex<double>(-0.1901597954359592, -0.2294955549701665), std::complex<double>(-0.1864961465389693, -0.0486276177310768),
+    std::complex<double>(-0.0000762326746410, 0.0000118155774181), std::complex<double>(0.0000118903581604, -0.0000251324432498),
+    std::complex<double>(-0.0002204197663391, -0.0000213776348027), std::complex<double>(0.0001477083861977, 0.0000599750510518),
+    std::complex<double>(-0.0003281057522772, -0.0000770207588466), std::complex<double>(0.0003478997686964, 0.0001481982639746),
+    std::complex<double>(-0.0000625695757282, 0.0000249138990722), std::complex<double>(-0.0000960097542525, 0.0002521364065803),
+    std::complex<double>(0.0001275344578325, 0.0000652362392482), std::complex<double>(-0.0003113309221942, 0.0001956734476566),
+    std::complex<double>(0.0029807707669629, -0.0003262084082071), std::complex<double>(0.0001639620574332, -0.0000266272685197),
+    std::complex<double>(0.0076282580587895, 0.0026614359017468), std::complex<double>(-0.0044850263974801, -0.0058337192660638),
+    std::complex<double>(0.0124258438959177, 0.0067985224235178), std::complex<double>(-0.0126349778957970, -0.0100656881493938),
+    std::complex<double>(0.0059031372522229, 0.0008660479915339), std::complex<double>(0.0039660364524413, -0.0100356333791398),
+    std::complex<double>(-0.0020520685193773, -0.0028564379463666), std::complex<double>(0.0121039958869239, -0.0059701468961263),
+    std::complex<double>(-0.0229975846564195, 0.0010565261888195), std::complex<double>(0.0019573207027441, 0.0050550600926414),
+    std::complex<double>(-0.0682274156850413, -0.0758159820140411), std::complex<double>(0.0497303968865466, 0.1019681987654797),
+    std::complex<double>(-0.1757936183439326, -0.1363710820472197), std::complex<double>(0.1765450269056824, 0.1555919358121995),
+    std::complex<double>(-0.1541299429420569, -0.0281422177614844), std::complex<double>(0.0816399676454817, 0.0691599035109852),
+    std::complex<double>(-0.0235110916473515, 0.0306385386726702), std::complex<double>(-0.0474273292450285, 0.0116831908947225),
+    std::complex<double>(0.0333560984394624, -0.0009767086536162), std::complex<double>(0.0141704479374002, -0.0205386534626779),
+    std::complex<double>(0.0562541280098909, 0.0743149092143081), std::complex<double>(-0.0226634801339250, -0.1439026188572270),
+    std::complex<double>(0.1238595124159999, 0.1766108700786397), std::complex<double>(-0.1307647072780430, -0.2090615438301942),
+    std::complex<double>(0.1557916917691289, 0.0646351862895731), std::complex<double>(0.0170294191358757, -0.1027926845803498),
+    std::complex<double>(0.0543537332385954, -0.0366524906364179), std::complex<double>(0.1127180664279469, -0.0176607923511174),
+    std::complex<double>(-0.0126732549319889, 0.0002042370658763), std::complex<double>(-0.0101360135082899, 0.0114084024114141),
+    std::complex<double>(-0.0102147881225462, -0.0176848554302252), std::complex<double>(-0.0051268936720694, 0.0527621533959941),
+    std::complex<double>(-0.0110701836450407, -0.0593085026046026), std::complex<double>(0.0140598301629874, 0.0738668439833535),
+    std::complex<double>(-0.0389912915621699, -0.0301364165752433), std::complex<double>(-0.0462331759359031, 0.0405864871628086),
+    std::complex<double>(-0.0251598701859194, 0.0115712688652445), std::complex<double>(-0.0563476280247398, 0.0079787883434624)
+};
diff --git a/CEP/Calibration/ElementResponse/src/ElementResponse.cc b/CEP/Calibration/ElementResponse/src/ElementResponse.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1324bfd4be5479bbadbd685bad2362e4d4c260fc
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/ElementResponse.cc
@@ -0,0 +1,157 @@
+//# ElementResponse.cc: Functions to compute the (idealized) response of a LOFAR
+//# LBA or HBA dual dipole antenna.
+//#
+//# Copyright (C) 2011
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <ElementResponse/ElementResponse.h>
+#include <cmath>
+
+// The coefficients are kept in an unnamed namespace which effectively makes
+// them invisible outside this translation unit.
+namespace
+{
+// PI / 2.0
+const double pi_2 = 1.570796326794896619231322;
+
+#include "DefaultCoeffLBA.cc"
+#include "DefaultCoeffHBA.cc"
+}
+
+namespace LOFAR
+{
+
+void element_response_lba(double freq, double theta, double phi,
+    std::complex<double> (&response)[2][2])
+{
+    element_response(freq, theta, phi, response, default_lba_freq_center,
+        default_lba_freq_range, default_lba_coeff_shape, default_lba_coeff);
+}
+
+void element_response_hba(double freq, double theta, double phi,
+    std::complex<double> (&response)[2][2])
+{
+    element_response(freq, theta, phi, response, default_hba_freq_center,
+        default_hba_freq_range, default_hba_coeff_shape, default_hba_coeff);
+}
+
+void element_response(double freq, double theta, double phi,
+    std::complex<double> (&response)[2][2], double freq_center,
+    double freq_range, const unsigned int (&coeff_shape)[3],
+    const std::complex<double> coeff[])
+{
+    // Initialize the response to zero.
+    response[0][0] = 0.0;
+    response[0][1] = 0.0;
+    response[1][0] = 0.0;
+    response[1][1] = 0.0;
+
+    // Clip directions below the horizon.
+    if(theta >= pi_2)
+    {
+        return;
+    }
+
+    const unsigned int nHarmonics  = coeff_shape[0];
+    const unsigned int nPowerTheta = coeff_shape[1];
+    const unsigned int nPowerFreq  = coeff_shape[2];
+
+    // The model is parameterized in terms of a normalized frequency in the
+    // range [-1, 1]. The appropriate conversion is taken care of below.
+    freq = (freq - freq_center) / freq_range;
+
+    // The variables sign and kappa are used to compute the value of kappa
+    // mentioned in the description of the beam model [kappa = (-1)^k * (2 * k
+    //+ 1)] incrementally.
+    int sign = 1, kappa = 1;
+
+    std::complex<double> P[2], Pj[2];
+    for(unsigned int k = 0; k < nHarmonics; ++k)
+    {
+        // Compute the (diagonal) projection matrix P for the current harmonic.
+        // This requires the evaluation of two polynomials in theta and freq (of
+        // degree nPowerTheta in theta and nPowerFreq in freq), one for each
+        // element of P. The polynomials are evaluated using Horner's rule.
+
+        // Horner's rule requires backward iteration of the coefficients, so
+        // move the iterator to the first location past the end of the block of
+        // coefficients for the current harmonic (k).
+        coeff += nPowerTheta * nPowerFreq * 2;
+
+        // Evaluate the polynomial. Note that the iterator is always decremented
+        // before it is dereferenced, using the prefix operator--. After
+        // evaluation of the polynomial, the iterator points exactly to the
+        // beginning of the block of coefficients for the current harmonic (k),
+        // that is, all the decrements together exactly cancel the increment
+        // aplied above.
+
+        // Evaluate the highest order term. Note that the order of the
+        // assigments is important because of the iterator decrement, i.e. P[1]
+        // should be assigned first.
+        P[1] = *--coeff;
+        P[0] = *--coeff;
+
+        for(unsigned int i = 0; i < nPowerFreq - 1; ++i)
+        {
+            P[1] = P[1] * freq + *--coeff;
+            P[0] = P[0] * freq + *--coeff;
+        }
+
+        // Evaluate the remaining terms.
+        for(unsigned int j = 0; j < nPowerTheta - 1; ++j)
+        {
+            Pj[1] = *--coeff;
+            Pj[0] = *--coeff;
+
+            for(unsigned int i = 0; i < nPowerFreq - 1; ++i)
+            {
+                Pj[1] = Pj[1] * freq + *--coeff;
+                Pj[0] = Pj[0] * freq + *--coeff;
+            }
+
+            P[1] = P[1] * theta + Pj[1];
+            P[0] = P[0] * theta + Pj[0];
+        }
+
+        // Because the increment and decrements cancel, the iterator points to
+        // the same location as at the beginning of this iteration of the outer
+        // loop. The next iteration should use the coefficients for the next
+        // harmonic (k), so we move the iterator to the start of that block.
+        coeff += nPowerTheta * nPowerFreq * 2;
+
+        // Compute the Jones matrix for the current harmonic, by rotating P over
+        // kappa * az, and add it to the result.
+        const double angle = sign * kappa * phi;
+        const double caz = std::cos(angle);
+        const double saz = std::sin(angle);
+
+        response[0][0] += caz * P[0];
+        response[0][1] += -saz * P[1];
+        response[1][0] += saz * P[0];
+        response[1][1] += caz * P[1];
+
+        // Update sign and kappa.
+        sign = -sign;
+        kappa += 2;
+    }
+}
+
+} //# namespace LOFAR
diff --git a/CEP/Calibration/ElementResponse/src/convert_coeff.py b/CEP/Calibration/ElementResponse/src/convert_coeff.py
new file mode 100755
index 0000000000000000000000000000000000000000..03618ec0e31c0758365ad459748fb3bfcb6a40f3
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/convert_coeff.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+
+# Script to convert an ASCII beam model coefficient file to a .cc file for
+# inclusion in the library. Whenever the beam model coefficients file are
+# updated, this script can be run to automatically update the corresponding .cc
+# files.
+#
+# $Id$
+
+import sys
+import time
+import re
+from functools import reduce
+
+def flat_index(shape, index):
+    """
+    Compute the flat index of the element with the provided (N-dimensional)
+    index of an N-dimensional array with the provided shape. Row-major order
+    (or "C"-order) is assumed in the computation of the flattened index. The
+    index is range checked against the provided shape.
+    """
+
+    assert len(shape) == len(index)
+
+    if len(shape) == 0:
+        return 0
+
+    assert index[0] < shape[0]
+    flat = index[0]
+
+    for i in range(1, len(shape)):
+        assert index[i] < shape[i]
+        flat *= shape[i]
+        flat += index[i]
+
+    return flat
+
+def regex(name, type, signed = True):
+    """
+    Return a regular expression to match a (possibly signed) int or float, using
+    the named group syntax. The matching group will by assigned the provided
+    name.
+    """
+
+    expr = None
+    if type == "int":
+        expr = "\d+"
+    elif type == "float":
+        expr = "(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?"
+
+    assert expr, "unknown type: %s" % type
+
+    if signed:
+        expr = "[-+]?" + expr
+
+    return "(?P<%s>%s)" % (name, expr)
+
+def main(args):
+    print("converting %s -> %s (variable name: %s)" % (args[0], args[1], args[2]))
+
+    HEADER, COEFF = list(range(2))
+    state = HEADER
+
+    shape = None
+    coeff = None
+    freqAvg = None
+    freqRange = None
+
+    line_no = 0
+    count = 0
+
+    fin = open(args[0], "r")
+    for line in fin:
+        # Remove leading and trailing whitespace.
+        line = line.strip()
+
+        # Skip empty lines and comments.
+        if len(line) == 0 or line[0] == "#":
+            line_no += 1
+            continue
+
+        # Parse header information.
+        if state == HEADER:
+            match = re.match("^d\s+%s\s+k\s+%s\s+pwrT\s+%s\s+pwrF\s+%s\s+"
+                "freqAvg\s+%s\s+freqRange\s+%s$" % (regex("d", "int", False),
+                regex("k", "int", False), regex("pwrT", "int", False),
+                regex("pwrF", "int", False), regex("freqAvg", "float", False),
+                regex("freqRange", "float", False)), line)
+            assert match, "unable to parse header: \"%s\"" % line
+
+            shape = (int(match.group("k")), int(match.group("pwrT")),
+                int(match.group("pwrF")), int(match.group("d")))
+            assert shape[3] == 2, "unsupported array shape, expected d == 2"
+
+            size = reduce(lambda x, y: x * y, shape)
+            print("coefficient array shape:", shape, "(%d total)" % size)
+
+            freqAvg = match.group("freqAvg")
+            freqRange = match.group("freqRange")
+
+            coeff = [None for x in range(size)]
+            state = COEFF
+
+        # Parse coefficients.
+        elif state == COEFF:
+            match = re.match("^%s\s+%s\s+%s\s+%s\s+%s\s+%s$"
+                % (regex("d", "int", False), regex("k", "int", False),
+                regex("pwrT", "int", False), regex("pwrF", "int", False),
+                regex("re", "float"), regex("im", "float")), line)
+            assert match, "unable to parse line #%d" % line_no
+
+            d = int(match.group("d"))
+            k = int(match.group("k"))
+            pwrT = int(match.group("pwrT"))
+            pwrF = int(match.group("pwrF"))
+
+            index = flat_index(shape, (k, pwrT, pwrF, d))
+            coeff[index] = "std::complex<double>(%s, %s)" % (match.group("re"), match.group("im"))
+
+            count += 1
+
+        line_no += 1
+
+    fin.close()
+    assert not (coeff is None) and all(coeff)
+
+    # Write the output.
+    fout = open(args[1], "w")
+
+    print("// Beam model coefficients converted by convert_coeff.py.", file=fout)
+    print("// Conversion performed on %s UTC using: " % time.strftime("%Y/%m/%d/%H:%M:%S", time.gmtime()), file=fout)
+    print("//     convert_coeff.py %s %s %s" % (args[0], args[1], args[2]), file=fout)
+    print(file=fout)
+    print("#include <complex>", file=fout)
+    print(file=fout)
+    print("// Center frequency, and frequency range for which the beam model coefficients", file=fout)
+    print("// are valid. The beam model is parameterized in terms of a normalized", file=fout)
+    print("// frequency f' in the range [-1.0, 1.0]. The appropriate conversion is:", file=fout)
+    print("//", file=fout)
+    print("//     f' = (f - center) / range", file=fout)
+    print("//", file=fout)
+    print("const double %s_freq_center = %s;" % (args[2], freqAvg), file=fout)
+    print("const double %s_freq_range = %s;" % (args[2], freqRange), file=fout)
+    print(file=fout)
+    print("// Shape of the coefficient array: %dx%dx%dx2 (the size of the last dimension is" % (shape[0], shape[1], shape[2]), file=fout)
+    print("// implied, and always equal to 2).", file=fout)
+    print("//", file=fout)
+    print("const unsigned int %s_coeff_shape[3] = {%d, %d, %d};" % (args[2], shape[0], shape[1], shape[2]), file=fout)
+    print(file=fout)
+    print("// The array of coefficients in row-major order (\"C\"-order).", file=fout)
+    print("//", file=fout)
+    print("const std::complex<double> %s_coeff[%d] = {" % (args[2], len(coeff)), file=fout)
+
+    i = 0
+    while i < len(coeff):
+        if i + 2 < len(coeff):
+            print("    %s, %s," % (coeff[i], coeff[i + 1]), file=fout)
+            i += 2
+        elif i + 2 == len(coeff):
+            print("    %s, %s" % (coeff[i], coeff[i + 1]), file=fout)
+            i += 2
+        else:
+            print("    %s" % coeff[i], file=fout)
+            i += 1
+
+    print("};", file=fout)
+
+    fout.close()
+
+if __name__ == "__main__":
+    if len(sys.argv) != 4:
+        print("convert a beam model coefficient (.coeff) file to a C++ (.cc) file.")
+        print("usage: convert_coeff.py <input-file> <output-file> <variable-name>")
+        sys.exit(1)
+
+    main(sys.argv[1:])
diff --git a/CEP/Calibration/ElementResponse/src/element_beam_HBA.coeff b/CEP/Calibration/ElementResponse/src/element_beam_HBA.coeff
new file mode 100644
index 0000000000000000000000000000000000000000..f2d39e70295da200b3f65c26328ab30f34740a02
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/element_beam_HBA.coeff
@@ -0,0 +1,101 @@
+d 2 k 2 pwrT 5 pwrF 5 freqAvg 180e6 freqRange 60e6
+0 0 0 0 0.9989322499459223 0.0003305895124867 
+1 0 0 0 1.0030546028872600 0.0002157249025076 
+0 1 0 0 -0.0004916757488397 0.0000266213616248 
+1 1 0 0 0.0006516553273188 -0.0000433166563288 
+0 0 1 0 0.0249658150445263 -0.0122024663463393 
+1 0 1 0 -0.0917825091832822 -0.0062606338208358 
+0 1 1 0 0.0162495046881796 -0.0010736997976255 
+1 1 1 0 -0.0175635905033026 0.0012997068962173 
+0 0 2 0 -0.6730977722052307 0.0940030437973656 
+1 0 2 0 0.3711597596859299 0.0557089394867947 
+0 1 2 0 -0.1492097398080444 0.0123735410547393 
+1 1 2 0 0.1393121453502456 -0.0121117146246749 
+0 0 3 0 0.0665319393516388 -0.1418009730472832 
+1 0 3 0 -0.7576728614553603 -0.0472040122949963 
+0 1 3 0 0.1567990061437258 -0.0143275575385193 
+1 1 3 0 -0.1043118778001582 0.0106756004832779 
+0 0 4 0 0.1121669548152054 0.0504713119323919 
+1 0 4 0 0.1882531376700409 0.0088411256350159 
+0 1 4 0 -0.0459124372023251 0.0044990718645418 
+1 1 4 0 0.0135433541303599 -0.0021789296923529 
+0 0 0 1 0.0003002209532403 0.0007909077657054 
+1 0 0 1 0.0022051270911392 0.0003834815341981 
+0 1 0 1 -0.0004357897643121 0.0000320567996700 
+1 1 0 1 0.0005818285824826 -0.0001021069650381 
+0 0 1 1 -0.0083709499453879 -0.0289759752488368 
+1 0 1 1 -0.0689260153643395 -0.0111348626546314 
+0 1 1 1 0.0138897851110661 -0.0014876219938565 
+1 1 1 1 -0.0150211436594772 0.0029712291209158 
+0 0 2 1 0.2119250520015808 0.2155514942677135 
+1 0 2 1 0.6727380529527980 0.0989550572104158 
+0 1 2 1 -0.1217628319418324 0.0222643129255504 
+1 1 2 1 0.1108579917761457 -0.0262986164183475 
+0 0 3 1 -0.1017024786435272 -0.3302620837788515 
+1 0 3 1 -0.5600906156274197 -0.0797555201430585 
+0 1 3 1 0.1151024257152241 -0.0225518489392044 
+1 1 3 1 -0.0593437249231851 0.0216080058910987 
+0 0 4 1 0.0066968933526899 0.1181452711088882 
+1 0 4 1 0.0981630367567397 0.0129921405004959 
+0 1 4 1 -0.0306136798186735 0.0064963361606382 
+1 1 4 1 -0.0046440676338940 -0.0037281688158807 
+0 0 0 2 -0.0003856663268042 0.0008435910525861 
+1 0 0 2 0.0004887765294093 0.0002777796480946 
+0 1 0 2 -0.0001047488648808 -0.0000302146563592 
+1 1 0 2 0.0001593350153828 -0.0000879125663990 
+0 0 1 2 0.0116296166994115 -0.0307342946951178 
+1 0 1 2 -0.0171249717275797 -0.0080642275561593 
+0 1 1 2 0.0031705620225488 0.0004838463688512 
+1 1 1 2 -0.0034418973689263 0.0024603729467258 
+0 0 2 2 -0.0419944347289523 0.2355624543349744 
+1 0 2 2 0.1917656461134636 0.0732470381581913 
+0 1 2 2 -0.0273147374272124 0.0098595182007132 
+1 1 2 2 0.0208992817013466 -0.0205929453727953 
+0 0 3 2 0.0889729243872774 -0.3406964719938829 
+1 0 3 2 -0.1342560801672904 -0.0515926960946038 
+0 1 3 2 0.0142781186223020 -0.0057037138045721 
+1 1 3 2 0.0151043140114779 0.0141435752121475 
+0 0 4 2 -0.0347327225501659 0.1186585563636635 
+1 0 4 2 0.0102831315790362 0.0046275244914932 
+0 1 4 2 -0.0006372791846825 0.0008894047150233 
+1 1 4 2 -0.0181611528840412 -0.0011106177431486 
+0 0 0 3 -0.0000699366665322 0.0005136144371953 
+1 0 0 3 0.0001520602842105 0.0001303481681886 
+0 1 0 3 -0.0000141882506567 -0.0000941521783975 
+1 1 0 3 -0.0000004226298134 -0.0000245060763932 
+0 0 1 3 0.0012408055399100 -0.0191295543986957 
+1 0 1 3 -0.0051031652662961 -0.0037143632875100 
+0 1 1 3 0.0003028387544878 0.0026905629457281 
+1 1 1 3 0.0006768121359769 0.0005901486396051 
+0 0 2 3 0.0048918921441903 0.1588912409502319 
+1 0 2 3 0.0575369727210951 0.0344677222786687 
+0 1 2 3 -0.0002152227668601 -0.0089220757225133 
+1 1 2 3 -0.0074792188817697 -0.0043562231368076 
+0 0 3 3 -0.0149335262655201 -0.2084962323582034 
+1 0 3 3 -0.0327252678958813 -0.0172371907472848 
+0 1 3 3 -0.0057143555179676 0.0141142700941743 
+1 1 3 3 0.0251435557201315 -0.0005753615445942 
+0 0 4 3 0.0070209144233666 0.0689639468490938 
+1 0 4 3 -0.0020239346031291 -0.0025499069613344 
+0 1 4 3 0.0032325387394458 -0.0048123509184894 
+1 1 4 3 -0.0136340313457176 0.0021185000810664 
+0 0 0 4 0.0000512381993616 0.0001550785137302 
+1 0 0 4 0.0000819244737818 0.0000466470412396 
+0 1 0 4 -0.0000177429496833 -0.0000561890408003 
+1 1 0 4 -0.0000018388829279 0.0000032387726477 
+0 0 1 4 -0.0022414352263751 -0.0060474723525871 
+1 0 1 4 -0.0024377933436567 -0.0012852163337395 
+0 1 1 4 0.0004634797107989 0.0016976603895716 
+1 1 1 4 0.0003344773954073 -0.0001499932789294 
+0 0 2 4 0.0241014578366618 0.0547046570516960 
+1 0 2 4 0.0219986510834463 0.0112189146988984 
+0 1 2 4 -0.0012019994038721 -0.0079939660050373 
+1 1 2 4 -0.0035807498769946 0.0014801422733613 
+0 0 3 4 -0.0362395089905272 -0.0661322227928722 
+1 0 3 4 -0.0141568558526096 -0.0042676979206835 
+0 1 3 4 0.0004475745352473 0.0102135659618127 
+1 1 3 4 0.0090474375150397 -0.0032177128650026 
+0 0 4 4 0.0132702874173192 0.0207916487187541 
+1 0 4 4 0.0004387107229914 -0.0017223838914815 
+0 1 4 4 0.0001287985092565 -0.0032079544559908 
+1 1 4 4 -0.0045503800737417 0.0015366231416036 
diff --git a/CEP/Calibration/ElementResponse/src/element_beam_LBA.coeff b/CEP/Calibration/ElementResponse/src/element_beam_LBA.coeff
new file mode 100644
index 0000000000000000000000000000000000000000..7259a7d7cecfe9c908a07b25ac42b6c85d9f3d45
--- /dev/null
+++ b/CEP/Calibration/ElementResponse/src/element_beam_LBA.coeff
@@ -0,0 +1,101 @@
+d 2 k 2 pwrT 5 pwrF 5 freqAvg 55e6 freqRange 45e6
+ 0 0 0 0        0.9982445079290715     0.0000650863154389  
+ 1 0 0 0        1.0006230902158257    -0.0022053287681416  
+ 0 1 0 0       -0.0000762326746410     0.0000118155774181  
+ 1 1 0 0        0.0000118903581604    -0.0000251324432498  
+ 0 0 1 0        0.0550606068782634     0.0011958385659938  
+ 1 0 1 0       -0.0160912944232080     0.0703645376267940  
+ 0 1 1 0        0.0029807707669629    -0.0003262084082071  
+ 1 1 1 0        0.0001639620574332    -0.0000266272685197  
+ 0 0 2 0       -0.8624889445327827    -0.1522883072804402  
+ 1 0 2 0       -0.0386800869486029    -0.7569350701887934  
+ 0 1 2 0       -0.0229975846564195     0.0010565261888195  
+ 1 1 2 0        0.0019573207027441     0.0050550600926414  
+ 0 0 3 0        0.4039567648146965     0.0810473144253429  
+ 1 0 3 0       -0.0350803390479135     0.5214591717801087  
+ 0 1 3 0        0.0333560984394624    -0.0009767086536162  
+ 1 1 3 0        0.0141704479374002    -0.0205386534626779  
+ 0 0 4 0       -0.0756250564248881     0.0056622911723172  
+ 1 0 4 0       -0.1267444401630109    -0.0349676272376008  
+ 0 1 4 0       -0.0126732549319889     0.0002042370658763  
+ 1 1 4 0       -0.0101360135082899     0.0114084024114141  
+ 0 0 0 1        0.0001002692200362     0.0006838211278268  
+ 1 0 0 1       -0.0003660049052840    -0.0008418920419220  
+ 0 1 0 1       -0.0002204197663391    -0.0000213776348027  
+ 1 1 0 1        0.0001477083861977     0.0000599750510518  
+ 0 0 1 1        0.0033849565901213    -0.0244636379385135  
+ 1 0 1 1        0.0234264238829944     0.0084068836453700  
+ 0 1 1 1        0.0076282580587895     0.0026614359017468  
+ 1 1 1 1       -0.0044850263974801    -0.0058337192660638  
+ 0 0 2 1        0.0891332399420108     0.1876527151756476  
+ 1 0 2 1       -0.1012363483900640    -0.1975118891151966  
+ 0 1 2 1       -0.0682274156850413    -0.0758159820140411  
+ 1 1 2 1        0.0497303968865466     0.1019681987654797  
+ 0 0 3 1        0.2232030356124932    -0.2248154851829713  
+ 1 0 3 1        0.4704343293662089    -0.3552101485419532  
+ 0 1 3 1        0.0562541280098909     0.0743149092143081  
+ 1 1 3 1       -0.0226634801339250    -0.1439026188572270  
+ 0 0 4 1       -0.1793752883639813     0.0720222655359702  
+ 1 0 4 1       -0.2678542619793421     0.3152115802895427  
+ 0 1 4 1       -0.0102147881225462    -0.0176848554302252  
+ 1 1 4 1       -0.0051268936720694     0.0527621533959941  
+ 0 0 0 2       -0.0010581424498791     0.0015237878543047  
+ 1 0 0 2        0.0007398729642721     0.0028468649470433  
+ 0 1 0 2       -0.0003281057522772    -0.0000770207588466  
+ 1 1 0 2        0.0003478997686964     0.0001481982639746  
+ 0 0 1 2        0.0557107413978542    -0.0634701730653090  
+ 1 0 1 2       -0.0139549526991330    -0.1175401658864208  
+ 0 1 1 2        0.0124258438959177     0.0067985224235178  
+ 1 1 1 2       -0.0126349778957970    -0.0100656881493938  
+ 0 0 2 2       -0.6404795825927633     0.7568775384981410  
+ 1 0 2 2        0.0767245154665722     1.3441875993523555  
+ 0 1 2 2       -0.1757936183439326    -0.1363710820472197  
+ 1 1 2 2        0.1765450269056824     0.1555919358121995  
+ 0 0 3 2        0.9646419509627557    -0.8095088593139815  
+ 1 0 3 2        0.1635280638865702    -1.4854352979459096  
+ 0 1 3 2        0.1238595124159999     0.1766108700786397  
+ 1 1 3 2       -0.1307647072780430    -0.2090615438301942  
+ 0 0 4 2       -0.3718069213271066     0.2275266747872172  
+ 1 0 4 2       -0.1372223722572021     0.4314989948093362  
+ 0 1 4 2       -0.0110701836450407    -0.0593085026046026  
+ 1 1 4 2        0.0140598301629874     0.0738668439833535  
+ 0 0 0 3       -0.0039458389254656    -0.0007048354913730  
+ 1 0 0 3        0.0007040177887611     0.0007856369612188  
+ 0 1 0 3       -0.0000625695757282     0.0000249138990722  
+ 1 1 0 3       -0.0000960097542525     0.0002521364065803  
+ 0 0 1 3        0.1336911750356096     0.0202651327657687  
+ 1 0 1 3       -0.0113385668361727    -0.0339262369086247  
+ 0 1 1 3        0.0059031372522229     0.0008660479915339  
+ 1 1 1 3        0.0039660364524413    -0.0100356333791398  
+ 0 0 2 3       -0.8758406699506004     0.3350237639226141  
+ 1 0 2 3        0.2824832769101577     0.6821307442669313  
+ 0 1 2 3       -0.1541299429420569    -0.0281422177614844  
+ 1 1 2 3        0.0816399676454817     0.0691599035109852  
+ 0 0 3 3        1.0331569921006993     0.0509705885336283  
+ 1 0 3 3        0.1501121326521990    -0.5193414816770609  
+ 0 1 3 3        0.1557916917691289     0.0646351862895731  
+ 1 1 3 3        0.0170294191358757    -0.1027926845803498  
+ 0 0 4 3       -0.3316657641578328    -0.1655909947939444  
+ 1 0 4 3       -0.2158100484836540     0.0614504774034524  
+ 0 1 4 3       -0.0389912915621699    -0.0301364165752433  
+ 1 1 4 3       -0.0462331759359031     0.0405864871628086  
+ 0 0 0 4       -0.0031701591723043    -0.0010521154166512  
+ 1 0 0 4       -0.0007213036752903    -0.0007227764008022  
+ 0 1 0 4        0.0001275344578325     0.0000652362392482  
+ 1 1 0 4       -0.0003113309221942     0.0001956734476566  
+ 0 0 1 4        0.0962263571740972     0.0440074333288440  
+ 1 0 1 4        0.0313595045238824     0.0230763038515351  
+ 0 1 1 4       -0.0020520685193773    -0.0028564379463666  
+ 1 1 1 4        0.0121039958869239    -0.0059701468961263  
+ 0 0 2 4       -0.3144282315609649    -0.2763869580286276  
+ 1 0 2 4       -0.1705959031354030    -0.0712085950559831  
+ 0 1 2 4       -0.0235110916473515     0.0306385386726702  
+ 1 1 2 4       -0.0474273292450285     0.0116831908947225  
+ 0 0 3 4        0.4715775965513117     0.5077361528286819  
+ 1 0 3 4        0.3847391427972284     0.1136717951238837  
+ 0 1 3 4        0.0543537332385954    -0.0366524906364179  
+ 1 1 3 4        0.1127180664279469    -0.0176607923511174  
+ 0 0 4 4       -0.1901597954359592    -0.2294955549701665  
+ 1 0 4 4       -0.1864961465389693    -0.0486276177310768  
+ 0 1 4 4       -0.0251598701859194     0.0115712688652445  
+ 1 1 4 4       -0.0563476280247398     0.0079787883434624
diff --git a/CEP/Calibration/ExpIon/CMakeLists.txt b/CEP/Calibration/ExpIon/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3c3ce32fcb31ba8fc15c850ebef103eb4194c7b8
--- /dev/null
+++ b/CEP/Calibration/ExpIon/CMakeLists.txt
@@ -0,0 +1,9 @@
+# $Id: CMakeLists.txt 14609 2009-12-07 09:10:48Z loose $
+
+# Do not split the following line, otherwise makeversion will fail!
+lofar_package(ExpIon 1.0 DEPENDS pyparameterset pyparmdb)
+
+include(LofarFindPackage)
+lofar_find_package(Boost REQUIRED COMPONENTS python thread)
+lofar_find_package(Casacore REQUIRED COMPONENTS python scimath)
+add_subdirectory(src)
diff --git a/CEP/Calibration/ExpIon/src/CMakeLists.txt b/CEP/Calibration/ExpIon/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7629de3ffc9090cb95112fe97534081129da3725
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/CMakeLists.txt
@@ -0,0 +1,43 @@
+# $Id: CMakeLists.txt 14508 2010-03-24 13:18:18Z vdtol $
+
+include(PythonInstall)
+
+lofar_add_bin_scripts(
+  calibrate-ion
+  readms-part.sh
+  readms-part.py
+  parmdbwriter.py)
+
+lofar_add_library(_baselinefitting MODULE baselinefitting.cc)
+
+set_target_properties(_baselinefitting PROPERTIES
+  PREFIX ""
+  LIBRARY_OUTPUT_DIRECTORY ${PYTHON_BUILD_DIR}/lofar/expion)
+
+# This is a quick-and-dirty fix to install the Python binding module in the
+# right place. It will now be installed twice, because lofar_add_library()
+# will install it in $prefix/$libdir
+install(TARGETS _baselinefitting
+  DESTINATION ${PYTHON_INSTALL_DIR}/lofar/expion)
+
+# Python modules.
+python_install(
+  __init__.py
+  io.py
+  format.py
+  ionosphere.py
+  acalc.py
+  client.py
+  sphere.py
+  error.py
+  mpfit.py
+  readms.py
+  parmdbmain.py
+  baselinefitting.py
+  repairGlobaldb.py
+  MMionosphere.py
+  fitClockTEC.py
+  PosTools.py
+  read_sagecal.py
+  DESTINATION lofar/expion)
+
diff --git a/CEP/Calibration/ExpIon/src/MMionosphere.py b/CEP/Calibration/ExpIon/src/MMionosphere.py
new file mode 100755
index 0000000000000000000000000000000000000000..ced85cbfadf7b55fd5665ac4d8438b2912b2a06a
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/MMionosphere.py
@@ -0,0 +1,674 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2007
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id$
+###############################################################################
+
+# import Python modules
+import sys
+import os
+from datetime import *
+from math import *
+import time
+import re
+import atexit
+
+# import 3rd party modules
+#from IPython.parallel import client
+from numpy import *
+#from pylab import *
+from numpy.linalg.linalg import inv
+from numpy import linalg
+import scipy.optimize
+
+# import user modules
+#from files import *
+from . import client
+from .acalc import *
+from . import sphere
+from .error import *
+import tables
+from . import PosTools
+
+###############################################################################
+
+class IonosphericModel:
+   """IonosphericModel class is the main interface to the functions impelmented in the ionosphere module"""
+
+   def __init__( self, ionmodel):
+
+      """
+      ionmodel is the hdf5 file
+      """
+
+      self.load_globaldb(ionmodel)
+
+   def load_globaldb ( self, ionmodel) :
+      self.hdf5 = tables.openFile(ionmodel, 'r+')
+      for varname in self.hdf5.root:
+         #print varname,"name:",varname.name
+         self.__dict__.update( [(varname.name, self.hdf5.getNode(self.hdf5.root, varname.name))] )
+
+      self.stations = self.hdf5.root.stations.cols.name
+      self.station_positions = self.hdf5.root.stations.cols.position
+      self.array_center = array( self.station_positions ).mean(axis=0).tolist()
+      if not 'array_center' in self.hdf5.root:
+         a_center=self.hdf5.createArray(self.hdf5.root, 'array_center', self.array_center)
+         a_center.flush()
+      self.array_center = self.hdf5.root.array_center
+      self.N_stations = len(self.stations)
+      self.pointing = self.hdf5.root.pointing
+      if not 'sources' in self.hdf5.root:
+         source_table = self.hdf5.createTable(self.hdf5.root, 'sources', {'name': tables.StringCol(40), 'position':tables.Float64Col(2)})
+         row = source_table.row
+         row['name'] = "Pointing"
+         row['position'] = list(self.pointing)
+         row.append()
+         source_table.flush()
+      if not 'timewidths' in self.hdf5.root:
+         self.timewidths=(self.times[1]-self.times[0])*ones(self.times.shape)
+      self.sources = self.hdf5.root.sources[:]['name']
+      self.source_positions = self.hdf5.root.sources[:]['position']
+      self.N_sources = len(self.sources)
+      self.N_piercepoints = self.N_sources * self.N_stations
+      
+
+
+      self.freqs = self.hdf5.root.freqs
+      self.polarizations = self.hdf5.root.polarizations
+      self.N_pol = len(self.polarizations)
+
+      self.phases = self.hdf5.root.phases
+      self.flags = self.hdf5.root.flags
+
+      #      for varname in ['amplitudes', 'Clock', 'TEC', 'TECfit', 'TECfit_white', 'offsets', \
+          #                         'rotation','times', 'timewidths', 'piercepoints', 'facets', 'facet_piercepoints', 'n_list', \
+      #    'STEC_facets'] :
+      #         if varname in self.hdf5.root:
+      #            self.__dict__.update( [(varname, self.hdf5.getNode(self.hdf5.root, varname))] )
+  
+            
+      
+   def calculate_piercepoints(self, time_steps = [], station_select=[],height = 200.e3):
+      if ( len( time_steps ) == 0 ):
+         n_list = list(range( self.times[:].shape[0]))
+      else:
+         n_list = time_steps
+      self.n_list = n_list
+      if 'n_list' in self.hdf5.root: self.hdf5.root.n_list.remove()
+      self.hdf5.createArray(self.hdf5.root, 'n_list', self.n_list)
+      if ( len( station_select ) == 0 ):
+         stat_select = list(range( self.stations[:].shape[0]))
+      else:
+         stat_select = station_select
+      self.stat_select = stat_select
+      if 'stat_select' in self.hdf5.root: self.hdf5.root.stat_select.remove()
+      self.hdf5.createArray(self.hdf5.root, 'stat_select', self.stat_select)
+
+
+      self.height = height
+
+      N_stations=len(stat_select)
+      if 'piercepoints' in self.hdf5.root: self.hdf5.root.piercepoints.remove()
+      description = {'positions':tables.Float64Col((self.N_sources, N_stations,2)), \
+                     'positions_xyz':tables.Float64Col((self.N_sources, N_stations,3)), \
+                     'zenith_angles':tables.Float64Col((self.N_sources, N_stations))}
+      self.piercepoints = self.hdf5.createTable(self.hdf5.root, 'piercepoints', description)
+      self.piercepoints.attrs.height = self.height
+      piercepoints_row = self.piercepoints.row
+      p = ProgressBar(len(n_list), "Calculating piercepoints: ")
+      for (n, counter) in zip(n_list, list(range(len(n_list)))):
+         p.update(counter)
+         
+         piercepoints=PosTools.getPiercePoints(self.times[n],self.source_positions,self.station_positions[:][self.stat_select[:]],height=self.height)
+         #piercepoints =  PiercePoints( self.times[ n ], self.pointing, self.array_center, self.source_positions, self.station_positions[:][self.stat_select[:]], height = self.height )
+         piercepoints_row['positions'] = piercepoints.positions
+         piercepoints_row['positions_xyz'] = piercepoints.positions_xyz
+         piercepoints_row['zenith_angles'] = piercepoints.zenith_angles
+         piercepoints_row.append()
+      self.piercepoints.flush()
+      p.finished()
+
+
+   def calculate_Sage_piercepoints(self, sage_group=0,time_steps = [], station_select=[],height = 200.e3):
+      if ( len( time_steps ) == 0 ):
+         n_list = list(range( self.times[:].shape[0]))
+      else:
+         n_list = time_steps
+      self.sage_n_list = n_list
+      if 'sage%d_n_list'%sage_group in self.hdf5.root: self.hdf5.removeNode('\sage%d_n_list'%sage_group)
+      self.hdf5.createArray(self.hdf5.root, 'sage%d_n_list'%sage_group, self.sage_n_list)
+      if ( len( station_select ) == 0 ):
+         stat_select = list(range( self.stations[:].shape[0]))
+      else:
+         stat_select = station_select
+      self.sage_stat_select = stat_select
+      if 'sage%d_stat_select'%sage_group in self.hdf5.root: self.hdf5.removeNode('\sage%d_stat_select'%sage_group)
+      self.hdf5.createArray(self.hdf5.root,'sage%d_stat_select'%sage_group, self.sage_stat_select)
+
+
+      self.sage_height = height
+
+      N_stations=len(stat_select)
+      if 'sage%d_piercepoints'%sage_group in self.hdf5.root: self.hdf5.removeNode('\sage%d_piercepoints'%sage_group)
+      description = {'positions':tables.Float64Col((self.N_sources, N_stations,2)), \
+                     'positions_xyz':tables.Float64Col((self.N_sources, N_stations,3)), \
+                     'zenith_angles':tables.Float64Col((self.N_sources, N_stations))}
+      self.sage_piercepoints = self.hdf5.createTable(self.hdf5.root,'sage%d_piercepoints'%sage_group, description)
+      self.sage_piercepoints.attrs.height = self.sage_height
+      piercepoints_row = self.sage_piercepoints.row
+      source_positions=self.hdf5.getNode('sage_radec%d'%sage_group)[:]
+      p = ProgressBar(len(n_list), "Calculating piercepoints: ")
+      for (n, counter) in zip(n_list, list(range(len(n_list)))):
+         p.update(counter)
+         
+         piercepoints=PosTools.getPiercePoints(self.times[n],source_positions,self.station_positions[:][self.stat_select[:]],height=self.sage_height)
+         #piercepoints =  PiercePoints( self.times[ n ], self.pointing, self.array_center, self.source_positions, self.station_positions[:][self.stat_select[:]], height = self.height )
+         piercepoints_row['positions'] = piercepoints.positions
+         piercepoints_row['positions_xyz'] = piercepoints.positions_xyz
+         piercepoints_row['zenith_angles'] = piercepoints.zenith_angles
+         piercepoints_row.append()
+      self.sage_piercepoints.flush()
+      p.finished()
+        
+   def calculate_basevectors(self, order = 15, beta = 5. / 3., r_0 = 1.):
+      self.order = order
+      self.beta = beta
+      self.r_0 = r_0
+      #N_stations = len(self.stations)
+      N_stations = len(self.stat_select[:])
+      N_sources = len(self.sources)
+      
+      N_piercepoints = N_stations * N_sources
+      P = eye(N_piercepoints) - ones((N_piercepoints, N_piercepoints)) / N_piercepoints
+      
+      self.C_list = []
+      self.U_list = []
+      self.S_list = []
+      p = ProgressBar(len(self.piercepoints), "Calculating base vectors: ")
+      for (piercepoints, counter) in zip(self.piercepoints, list(range(len(self.piercepoints)))):
+         p.update( counter )
+         Xp_table = reshape(piercepoints['positions_xyz'], (N_piercepoints, 3) )
+         
+         # calculate structure matrix
+         D = resize( Xp_table, ( N_piercepoints, N_piercepoints, 3 ) )
+         D = transpose( D, ( 1, 0, 2 ) ) - D
+         D2 = sum( D**2, 2 )
+         C = -(D2 / ( r_0**2 ) )**( beta / 2.0 )/2.0
+         self.C_list.append(C)
+
+         # calculate covariance matrix C
+         # calculate partial product for interpolation B
+         # reforce symmetry
+         
+         C = dot(dot(P, C ), P)
+
+         # eigenvalue decomposition
+         # reforce symmetry
+         # select subset of base vectors
+         [ U, S, V ] = linalg.svd( C )
+         U = U[ :, 0 : order ]
+         S = S[ 0 : order ]
+         self.U_list.append(U) 
+         self.S_list.append(S)
+      p.finished()
+
+   def fit_model  ( self) :
+      #N_stations = len(self.stations)
+      N_stations = len(self.stat_select[:])
+      N_times = len(self.times)
+      N_sources = len(self.sources)
+      N_pol = min(len(self.polarizations),2)
+      G = kron(eye( N_sources ), ( eye( N_stations ) - ones((N_stations, N_stations)) / N_stations))
+
+      if 'TECfit' in self.hdf5.root: self.hdf5.root.TECfit.remove()
+      self.TECfit = self.hdf5.createArray(self.hdf5.root, 'TECfit', zeros(self.TEC[:].shape))
+      
+      if 'TECfit_white' in self.hdf5.root: self.hdf5.root.TECfit_white.remove()
+      self.TECfit_white = self.hdf5.createArray(self.hdf5.root, 'TECfit_white', zeros(self.TEC[:].shape))
+      
+      self.offsets = zeros((len(self.n_list),N_pol))
+      p = ProgressBar(len(self.n_list), "Fitting phase screen: ")
+      za=self.piercepoints.cols.zenith_angles[:]
+      for i in range(len(self.n_list)) :
+         p.update( i )
+         U = self.U_list[i]
+         S = self.S_list[i]
+         for pol in range(N_pol) :
+            #print self.TEC[ self.n_list[i], :, :,pol].shape
+            TEC = multiply(self.TEC[:][ self.n_list[i], self.stat_select[:], :,pol].swapaxes(0,1),
+                           cos(za[i,:,:])).reshape( (N_sources * N_stations, 1) )
+            TECfit = dot(U, dot(inv(dot(U.T, dot(G, U))), dot(U.T, dot(G, TEC))))
+            TECfit_white = dot(U, dot(diag(1/S), dot(U.T, TECfit)))
+            self.offsets[i,pol] = TECfit[0] - dot(self.C_list[i][0,:], TECfit_white)
+            TECfit=reshape( TECfit,  (N_sources,N_stations) ).swapaxes(0,1)
+            TECfit_white= reshape( TECfit_white,(N_sources,N_stations)  ).swapaxes(0,1)
+            for istat,stat in enumerate(self.stat_select[:]):
+               self.TECfit[i, stat, : ,pol] = TECfit[istat,:]
+               self.TECfit_white[i,stat,: ,pol ] = TECfit_white[istat,:]
+      p.finished()      
+
+      self.TECfit_white.attrs.r_0 = self.r_0
+      self.TECfit_white.attrs.beta = self.beta
+      
+      if 'offsets' in self.hdf5.root: self.hdf5.root.offsets.remove()
+      self.hdf5.createArray(self.hdf5.root, 'offsets', self.offsets)
+
+   def get_TEC_pp(self,radec,pol=0,new_stat_select=None):
+      TEC_out=[]      
+      N_stations = len(self.stat_select[:])
+      N_sources = len(self.sources)
+      N_piercepoints = N_stations * N_sources
+      h=self.piercepoints.attrs.height
+      r_0 = self.TECfit_white.attrs.r_0
+      beta = self.TECfit_white.attrs.beta
+      pp_out=[]
+      am_out=[]
+      TEC_out=[]
+      stations=self.station_positions[:]
+      if not (new_stat_select is None):
+         stations=stations[new_stat_select]
+      for i in range(len(self.n_list)) :
+         if i%10==0:
+            sys.stdout.write(str(i)+'...')
+            sys.stdout.flush()                
+         time=self.times[i]
+         piercepoints=PosTools.getPiercePoints(time,radec.reshape((1,-1)),stations,height=h)
+         pp=piercepoints.positions_xyz
+         am=piercepoints.zenith_angles
+         pp_out.append(pp)
+         am_out.append(am)
+         Xp_table=reshape(self.piercepoints[i]['positions_xyz'], (N_piercepoints, 3))
+         #v=self.TECfit_white[ i, :, : ,pol][self.stat_select[:]].reshape((N_piercepoints,1))
+         v=self.TECfit_white[ i, :, : ,pol][self.stat_select[:]].reshape((N_piercepoints,1))
+         tecs=[]
+         for ist in range(stations[:].shape[0]):
+            tecs.append(get_interpolated_TEC_white(Xp_table,v,beta,r_0,pp[0,ist]))
+         TEC_out.append(tecs)
+      return array(pp_out),array(am_out),array(TEC_out)
+
+   def get_TEC_frame(self,time=0,pol=0,scale=1.1,steps=10):
+      N_stations = len(self.stat_select[:])
+      N_sources = len(self.sources)
+      N_piercepoints = N_stations * N_sources
+
+      h=self.piercepoints.attrs.height
+      r_0 = self.TECfit_white.attrs.r_0
+      beta = self.TECfit_white.attrs.beta
+      pp=self.piercepoints[time]['positions'].reshape(N_piercepoints,2)
+      Xp_table=reshape(self.piercepoints[time]['positions_xyz'], (N_piercepoints, 3))
+      v=self.TECfit_white[ time, :, : ,pol][self.stat_select[:]].reshape((N_piercepoints,1))
+      myxlim=[min(pp[:,0]),max(pp[:,0])]
+      myylim=[min(pp[:,1]),max(pp[:,1])]
+      diff=(myxlim[1]-myxlim[0])*(scale-1.)*0.5
+      myxlim[0]-=diff
+      myxlim[1]+=diff
+      xsize=(myxlim[1]-myxlim[0])*scale/(steps+1)
+      myxlim[1]+=xsize
+      diff=(myylim[1]-myylim[0])*(scale-1.)*0.5
+      myylim[0]-=diff
+      myylim[1]+=diff
+      ysize=(myylim[1]-myylim[0])*scale/(steps+1)
+      myylim[1]+=ysize
+      length=sqrt(dot(Xp_table[0],Xp_table[0].T))
+      #print "length",length,myxlim,myylim,xsize,ysize
+      iy=0
+      ix=0
+      phi=zeros((steps,steps),dtype=float)
+      for lat in arange(myylim[0],myylim[1],ysize): 
+         for lon in arange(myxlim[0],myxlim[1],xsize): 
+            xyzpp=[cos(lat)*cos(lon)*length,cos(lat)*sin(lon)*length,sin(lat)*length]
+            #print "my",ix,iy,lon,lat,xyzpp
+            #print Xp_table[0]
+            phi[iy,ix]=get_interpolated_TEC_white(Xp_table,v,beta,r_0,xyzpp)
+            ix+=1
+         ix=0
+         iy+=1
+      return phi,arange(myxlim[0],myxlim[1],xsize),arange(myylim[0],myylim[1],ysize)
+
+   def make_movie( self, extent = 0, npixels = 100, vmin = 0, vmax = 0 ):
+      """
+      """
+
+      multiengine_furl =  os.environ['HOME'] + '/ipcluster/multiengine.furl'
+#      mec = client.MultiEngineClient( multiengine_furl )
+      mec = client.MultiEngineClient( )
+      task_furl =  os.environ['HOME'] + '/ipcluster/task.furl'
+      #tc = client.TaskClient( task_furl )
+      tc = client.TaskClient( )
+      N_stations = len(self.stations)
+      N_times = self.TECfit[:].shape[1]
+      N_sources = len(self.sources)
+      N_piercepoints = N_stations * N_sources
+      N_pol = len(self.polarizations)
+      R = 6378137
+      taskids = []
+      
+      print("Making movie...")
+      p = ProgressBar( len( self.n_list ), 'Submitting jobs: ' )
+      for i in range(len(self.n_list)) :
+         p.update(i)
+         Xp_table = reshape(self.piercepoints[i]['positions'], (N_piercepoints, 2) )
+         if extent > 0 :
+            w = extent/R/2
+         else :
+            w = 1.1*abs(Xp_table).max()
+         for pol in range(N_pol) :
+            v = self.TECfit[ i, :, : ,pol].reshape((N_piercepoints,1))
+            maptask = client.MapTask(calculate_frame, (Xp_table, v, self.beta, self.r_0, npixels, w) )
+            taskids.append(tc.run(maptask))
+      p.finished()
+      
+      if vmin == 0 and vmax == 0 :
+         vmin = self.TECfit.min()
+         vmax = self.TECfit.max()
+         vdiff = vmax - vmin
+         vmin = vmin - 0.1*vdiff
+         vmax = vmax + 0.1*vdiff
+
+      p = ProgressBar( len( self.n_list ), 'Fetch results: ' )
+      for i in range(len(self.n_list)) :
+         p.update(i)
+         clf()
+         for pol in range(N_pol) :
+            (phi,w) = tc.get_task_result(taskids.pop(0), block = True)
+            phi = phi + self.offsets[pol, i]
+            subplot(1, N_pol, pol+1)
+            w = w*R*1e-3
+            h_im = imshow(phi, interpolation = 'nearest', origin = 'lower', extent = (-w, w, -w, w), vmin = vmin, vmax = vmax )
+            h_axes = gca()
+            cl = h_im.get_clim()
+            TEC =  reshape( self.TEC[ pol, i, :, : ], N_piercepoints )
+            for j in range(N_piercepoints):
+               color = h_im.cmap(int(round((TEC[j]-cl[0])/(cl[1]-cl[0])*(h_im.cmap.N-1))))
+               plot(R*1e-3*Xp_table[j,0], R*1e-3*Xp_table[j,1], marker = 'o', markeredgecolor = 'k', markerfacecolor = color)
+            colorbar()
+            xlim(-w, w)
+            ylim(-w, w)
+         savefig('tmpfig%4.4i.png' % i)
+      p.finished()
+      os.system("mencoder mf://tmpfig????.png -o movie.mpeg -mf type=png:fps=3  -ovc lavc -ffourcc DX50 -noskip -oac copy")
+
+   def interpolate( self, facetlistfile ) :
+      
+      """
+      """
+      
+      #facetdbname = os.path.join(self.globaldb, 'facets')
+      #os.system( 'makesourcedb in=%s out=%s append=False' % (facetlistfile, facetdbname) )
+      
+      #patch_table = pt.table( os.path.join(facetdbname, 'SOURCES', 'PATCHES' ) )
+      
+      #if 'facets' in self.hdf5.root: self.hdf5.root.facets.remove()
+      #description = {'name': tables.StringCol(40), 'position':tables.Float64Col(2)}
+      #self.facets = self.hdf5.createTable(self.hdf5.root, 'facets', description)
+
+      #facet = self.facets.row
+      #for patch in patch_table :
+         #facet['name'] = patch['PATCHNAME']
+         #facet['position'] = array([patch['RA'], patch['DEC']])
+         #facet.append()
+      #self.facets.flush()
+      self.N_facets = len(self.facets)
+      
+      self.facet_names = self.facets[:]['name']
+      self.facet_positions = self.facets[:]['position']
+
+      print(self.n_list)
+      if 'STEC_facets' in self.hdf5.root: self.hdf5.root.STEC_facets.remove()
+      self.STEC_facets = self.hdf5.createCArray(self.hdf5.root, 'STEC_facets', tables.Float32Atom(), shape = (self.N_pol, self.n_list[:].shape[0],  self.N_facets, self.N_stations))
+
+      #if 'facet_piercepoints' in self.hdf5.root: self.hdf5.root.facet_piercepoints.remove()
+      #description = {'positions':tables.Float64Col((self.N_facets, self.N_stations,2)), \
+                     #'positions_xyz':tables.Float64Col((self.N_facets, self.N_stations,3)), \
+                     #'zenith_angles':tables.Float64Col((self.N_facets, self.N_stations))}
+      #self.facet_piercepoints = self.hdf5.createTable(self.hdf5.root, 'facet_piercepoints', description)
+      #height = self.piercepoints.attrs.height
+      #facet_piercepoints_row = self.facet_piercepoints.row
+      #print "Calculating facet piercepoints..."
+      #for n in self.n_list:
+         #piercepoints = PiercePoints( self.times[ n ], self.pointing, self.array_center, self.facet_positions, self.station_positions, height = height )
+         #facet_piercepoints_row['positions'] = piercepoints.positions
+         #facet_piercepoints_row['positions_xyz'] = piercepoints.positions_xyz
+         #facet_piercepoints_row['zenith_angles'] = piercepoints.zenith_angles
+         #facet_piercepoints_row.append()
+      #self.facet_piercepoints.flush()
+
+      r_0 = self.TECfit_white.attrs.r_0
+      beta = self.TECfit_white.attrs.beta
+      
+      for facet_idx in range(self.N_facets) :
+         for station_idx in range(self.N_stations):
+            for pol_idx in range(self.N_pol) :
+               TEC_list = []
+               for n in range(len(self.n_list)):
+                  p = self.facet_piercepoints[n]['positions_xyz'][facet_idx, station_idx,:]
+                  za = self.facet_piercepoints[n]['zenith_angles'][facet_idx, station_idx]
+                  Xp_table = reshape(self.piercepoints[n]['positions_xyz'], (self.N_piercepoints, 3) )
+                  v = self.TECfit_white[ pol_idx, n, :, : ].reshape((self.N_piercepoints,1))
+                  D2 = sum((Xp_table - p)**2,1)
+                  C = (D2 / ( r_0**2 ) )**( beta / 2. ) / -2.
+                  self.STEC_facets[pol_idx, n,  facet_idx, station_idx] = dot(C, v)/cos(za)
+
+def product(*args, **kwds):
+    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+    pools = list(map(tuple, args)) * kwds.get('repeat', 1)
+    result = [[]]
+    for pool in pools:
+        result = [x+[y] for x in result for y in pool]
+    for prod in result:
+        yield tuple(prod)
+
+def fillarray( a, v ) :
+   print(a.shape, a.chunkshape)
+   for idx in product(*[range(0, s, c) for s, c in zip(a.shape, a.chunkshape)]) :
+      s = tuple([slice(i,min(i+c,s)) for i,s,c in zip(idx, a.shape, a.chunkshape)])
+      a[s] = v
+   
+
+
+def calculate_frame(Xp_table, v, beta, r_0, npixels, w):
+   import numpy
+   phi = zeros((npixels,npixels))
+   N_piercepoints = Xp_table.shape[0]
+   P = eye(N_piercepoints) - ones((N_piercepoints, N_piercepoints)) / N_piercepoints
+   # calculate structure matrix
+   D = resize( Xp_table, ( N_piercepoints, N_piercepoints, 2 ) )
+   D = transpose( D, ( 1, 0, 2 ) ) - D
+   D2 = sum( D**2, 2 )
+   C = -(D2 / ( r_0**2 ) )**( beta / 2.0 )/2.0
+   C = dot(dot(P, C ), P)
+   v = dot(linalg.pinv(C), v)
+
+   for x_idx in range(0, npixels):
+      x = -w + 2*x_idx*w/( npixels-1 )  
+      for y_idx in range(0, npixels):
+         y = -w + 2*y_idx*w/(npixels-1)
+         #D2 = sum((6378452*(Xp_table - array([ x, y ])))**2,1)
+         D2 = sum(((Xp_table - array([ x, y ])))**2,1)
+         C = (D2 / ( r_0**2 ) )**( beta / 2. ) / -2.
+         phi[y_idx, x_idx] = dot(C, v)
+   return phi, w
+
+
+
+
+def get_interpolated_TEC(Xp_table,v,beta,r_0,pp):
+   N_piercepoints = Xp_table.shape[0]
+   n_axes=Xp_table.shape[1]
+   if n_axes!=pp.shape[0]:
+      print("wrong number of axes, original:",n_axes,"requested:",pp.shape[0])
+      return -1
+   P = eye(N_piercepoints) - ones((N_piercepoints, N_piercepoints)) / N_piercepoints
+   # calculate structure matrix
+   D = resize( Xp_table, ( N_piercepoints, N_piercepoints, n_axes ) )
+   D = transpose( D, ( 1, 0, 2 ) ) - D
+   D2 = sum( D**2, 2 )
+   C = -(D2 / ( r_0**2 ) )**( beta / 2.0 )/2.0
+   C = dot(dot(P, C ), P)
+   v = dot(linalg.pinv(C), v)
+   D2 = sum((Xp_table - pp)**2,axis=1)
+   C = (D2 / ( r_0**2 ) )**( beta / 2. ) / -2.
+   return dot(C, v)
+
+
+def get_interpolated_TEC_white(Xp_table,v,beta,r_0,pp):
+   D2 = sum((Xp_table - pp)**2,axis=1)
+   C = (D2 / ( r_0**2 ) )**( beta / 2. ) / -2.
+   return dot(C, v)
+
+
+
+
+def fit_phi_klmap_model( P, U_table = None, pza_table = None, phase_table = None, dojac = None):
+# TODO: calculate penalty terms of MAP estimator
+
+   # handle input parameters
+   if ( ( U_table == None ) or
+        ( pza_table == None ) or 
+        ( phase_table == None ) ):
+      return - 1, None, None
+
+   (antenna_count, source_count) = phase_table.shape
+
+   # calculate phases at puncture points
+   phi_table = dot( U_table, P ) / cos( aradians( pza_table ) )
+   phi_table = phi_table.reshape( (antenna_count, source_count) )
+
+   # calculate chi2 terms
+   chi_list = []
+   for source in range(source_count):
+      for i in range(antenna_count):
+         for j in range(i, antenna_count):
+            chi_list.append( mod( phi_table[i, source] - phi_table[j, source] - phase_table[i, source] + phase_table[j, source] + pi, 2*pi) - pi )
+
+   # make (normalized) chi2 array
+   chi_array = array( chi_list )
+
+   return 0, chi_array, None
+
+###############################################################################
+
+# gradient
+
+def phi_gradient_model( X, p ):
+  phi = dot( X, p )
+  return phi
+
+###############################################################################
+
+def phi_klmap_model( X, Xp_table, B_table, F_table, beta = 5. / 3., r_0 = 1. ):
+# B_table = (1/m)(1T)(C_table)(A_table)
+# F_table = ( Ci_table )( U_table )( P_table )
+
+  # input check
+  if ( len( shape( X ) ) == 1 ):
+    X_table = array( [ X ] )
+  elif ( len( shape( X ) ) == 2 ):
+    X_table = X
+
+  # calculate structure matrix
+  x_count = len( X_table )
+  p_count = len( Xp_table )
+  D_table = transpose( resize( X_table, ( p_count, x_count, 2 ) ), ( 1, 0, 2 ) )
+  D_table = D_table - resize( Xp_table, ( x_count, p_count, 2 ) )
+  D_table = add.reduce( D_table**2, 2 )
+  D_table = ( D_table / ( r_0**2 ) )**( beta / 2. )
+
+  # calculate covariance matrix
+  C_table = - D_table / 2.
+  C_table = transpose( transpose( C_table ) - ( add.reduce( C_table, 1 ) / float( p_count ) ) )
+  C_table = C_table - B_table
+
+  phi = dot( C_table, F_table )
+  phi = reshape( phi, ( x_count ) )
+  if ( len( phi ) == 1 ):
+    phi = phi[ 0 ]
+
+  return phi
+
+
+###############################################################################
+
+class PiercePoints:
+   
+   def __init__( self, time, pointing, array_center, source_positions, antenna_positions, height = 400.e3 ):
+      # source table radecs at observing epoch
+
+      # calculate Earth referenced coordinates of puncture points of array center towards pointing center
+      [ center_pxyz, center_pza ] = sphere.calculate_puncture_point_mevius( array_center, pointing, time, height = height )
+      self.center_p_geo_llh = sphere.xyz_to_geo_llh( center_pxyz, time )
+
+      # loop over sources
+      positions = []
+      positions_xyz = []
+      zenith_angles = []
+      
+      for k in range( len( source_positions ) ):
+         positions_xyz1 = []
+         positions1 = []
+         zenith_angles1 = []
+
+         # loop over antennas
+         for i in range( len( antenna_positions ) ):
+            # calculate Earth referenced coordinates of puncture points of antenna towards peeled source
+            [ pxyz, pza ] = sphere.calculate_puncture_point_mevius( antenna_positions[ i ], source_positions[ k ], time, height = height )
+            p_geo_llh = sphere.xyz_to_geo_llh( pxyz, time )
+
+            # calculate local angular coordinates of antenna puncture point ( x = East, y = North )
+            [ separation, angle ] = sphere.calculate_angular_separation( self.center_p_geo_llh[ 0 : 2 ], p_geo_llh[ 0 : 2 ] )
+            X = [ separation * sin( angle ), separation * cos( angle ) ]
+
+            # store model fit input data
+            positions1.append(X)
+            positions_xyz1.append( pxyz )
+            zenith_angles1.append( pza )
+         positions.append(positions1)
+         positions_xyz.append(positions_xyz1)
+         zenith_angles.append( zenith_angles1 )
+      
+      self.positions = array( positions ) 
+      self.positions_xyz = array( positions_xyz ) 
+      self.zenith_angles = array( zenith_angles )
+
+
+class ProgressBar:
+   
+   def __init__(self, length, message = ''):
+      self.length = length
+      self.current = 0
+      sys.stdout.write(message + '0%')
+      sys.stdout.flush()
+
+   def update(self, value):
+      while self.current < 2*int(50*value/self.length):
+         self.current += 2
+         if self.current % 10 == 0 :
+            sys.stdout.write(str(self.current) + '%')
+         else:
+            sys.stdout.write('.')
+         sys.stdout.flush()
+            
+   def finished(self):
+      self.update(self.length)
+      sys.stdout.write('\n')
+      sys.stdout.flush()
+      
diff --git a/CEP/Calibration/ExpIon/src/PosTools.py b/CEP/Calibration/ExpIon/src/PosTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..e883734b163fc9eb0f2605a1f878c1c59c27b066
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/PosTools.py
@@ -0,0 +1,272 @@
+from pyrap.measures import measures
+from math import *
+import pyrap.quanta as qa;
+import os
+from pyrap import tables as tab
+import numpy as np
+
+R_earth=6364.62e3;
+earth_ellipsoid_a = 6378137.0;
+earth_ellipsoid_a2 = earth_ellipsoid_a*earth_ellipsoid_a;
+earth_ellipsoid_b = 6356752.3142;
+earth_ellipsoid_b2 = earth_ellipsoid_b*earth_ellipsoid_b;
+earth_ellipsoid_e2 = (earth_ellipsoid_a2 - earth_ellipsoid_b2) / earth_ellipsoid_a2;
+posCS002=[3826577.1095  ,461022.900196, 5064892.758]
+
+def getMSinfo(MS=None):
+    print("getting info for",MS)
+    if MS is None:
+        print("No measurement set given")
+        return
+    if os.path.isdir(MS):
+        myMS=tab.table(MS)
+    else:
+        print("Do not understand the format of MS",MS,"bailing out")
+        return;
+    print("opened table",MS)
+    timerange=[np.amin(myMS.getcol('TIME_CENTROID')),np.amax(myMS.getcol('TIME_CENTROID'))]
+    timestep=myMS.getcell('INTERVAL',0)
+    
+    pointing= tab.table(myMS.getkeyword('FIELD')).getcell('PHASE_DIR',0);    
+    stations = tab.table(myMS.getkeyword('ANTENNA')).getcol('NAME')
+    station_pos = tab.table(myMS.getkeyword('ANTENNA')).getcol('POSITION')
+
+    return (timerange,timestep,pointing.flatten(),stations,station_pos)
+
+def getPPsimple(height=[450.e3,],mPosition=[0.,0.,0.],direction=[0.,0.,0.]):
+    '''get piercepoints for antenna position mPosition in m, direction ITRF in m on unit sphere and for array of heights, assuming a spherical Earth'''
+    height=np.array(height)
+    stX=mPosition[0]
+    stY=mPosition[1]
+    stZ=mPosition[2]
+    x=np.divide(stX,(R_earth+height))
+    y=np.divide(stY,(R_earth+height))
+    z=np.divide(stZ,(R_earth+height))
+    c = x*x + y*y + z*z - 1.0;
+   
+    dx=np.divide(direction[0],(R_earth+height))
+    dy=np.divide(direction[1],(R_earth+height))
+    dz=np.divide(direction[2],(R_earth+height))
+
+    a = dx*dx + dy*dy + dz*dz;
+    b = x*dx + y*dy  + z*dz;
+
+    alpha = (-b + np.sqrt(b*b - a*c))/a;
+
+    pp=np.zeros(height.shape+(3,))
+    pp[:,0]=stX+alpha*direction[0]
+    pp[:,1]=stY+alpha*direction[1]
+    pp[:,2]=stZ+alpha*direction[2]
+
+    am=np.divide(1.,pp[:,0]*dx+pp[:,1]*dy+pp[:,2]*dz)
+    return pp,am
+
+def getPPsimpleAngle(height=[450.e3,],mPosition=[0.,0.,0.],direction=[0.,0.,0.]):
+    '''get (lon,lat,h values) of piercepoints for antenna position mPosition in m, direction ITRF in m on unit sphere and for array of heights, assuming a spherical Earth'''
+    height=np.array(height)
+    stX=mPosition[0]
+    stY=mPosition[1]
+    stZ=mPosition[2]
+    x=np.divide(stX,(R_earth+height))
+    y=np.divide(stY,(R_earth+height))
+    z=np.divide(stZ,(R_earth+height))
+    
+    c = x*x + y*y + z*z - 1.0;
+   
+    dx=np.divide(direction[0],(R_earth+height))
+    dy=np.divide(direction[1],(R_earth+height))
+    dz=np.divide(direction[2],(R_earth+height))
+
+    a = dx*dx + dy*dy + dz*dz;
+    b = x*dx + y*dy  + z*dz;
+
+    alpha = (-b + np.sqrt(b*b - a*c))/a;
+
+
+    pp=np.zeros(height.shape+(3,))
+    pp[:,0]=stX+alpha*direction[0]
+    pp[:,1]=stY+alpha*direction[1]
+    pp[:,2]=stZ+alpha*direction[2]
+
+    am=np.divide(1.,pp[:,0]*dx+pp[:,1]*dy+pp[:,2]*dz)
+
+    ppl=np.zeros(height.shape+(3,))
+    ppl[:,0]=np.atan2(pp[:,1],pp[:0])
+    ppl[:,1]=np.atan2(pp[:,2],np.sqrt(pp[:0]*pp[:,0]+pp[:1]*pp[:,1]))
+    ppl[:,2]=heigth
+
+    return ppl,am
+
+def getPP(h=450e3,mPosition=[0.,0.,0.],direction=[0.,0.,0.]):
+    stationX = mPosition[0];
+    stationY = mPosition[1];
+    stationZ = mPosition[2];
+
+    ion_ellipsoid_a = earth_ellipsoid_a + h;
+    ion_ellipsoid_a2_inv = 1.0 / (ion_ellipsoid_a * ion_ellipsoid_a);
+    ion_ellipsoid_b = earth_ellipsoid_b + h;
+    ion_ellipsoid_b2_inv = 1.0 / (ion_ellipsoid_b * ion_ellipsoid_b);
+    
+    x = stationX/ion_ellipsoid_a;
+    y = stationY/ion_ellipsoid_a;
+    z = stationZ/ion_ellipsoid_b;
+    c = x*x + y*y + z*z - 1.0;
+
+    dx = direction [0]/ ion_ellipsoid_a;
+    dy = direction [1] / ion_ellipsoid_a;
+    dz = direction [2] / ion_ellipsoid_b;
+
+    a = dx*dx + dy*dy + dz*dz;
+    b = x*dx + y*dy  + z*dz;
+    alpha = (-b + sqrt(b*b - a*c))/a;
+    pp_x = stationX + alpha*direction[0];
+    pp_y = stationY + alpha*direction[1]
+    pp_z = stationZ + alpha*direction[2];
+
+    normal_x = pp_x * ion_ellipsoid_a2_inv;
+    normal_y = pp_y * ion_ellipsoid_a2_inv;
+    normal_z = pp_z * ion_ellipsoid_b2_inv;
+    norm_normal2 = normal_x*normal_x + normal_y*normal_y + normal_z*normal_z;
+    norm_normal = sqrt(norm_normal2);
+    sin_lat2 = normal_z*normal_z / norm_normal2;
+
+ 
+    g = 1.0 - earth_ellipsoid_e2*sin_lat2;
+    sqrt_g = sqrt(g);
+
+    M = earth_ellipsoid_b2 / ( earth_ellipsoid_a * g * sqrt_g );
+    N = earth_ellipsoid_a / sqrt_g;
+
+    local_ion_ellipsoid_e2 = (M-N) / ((M+h)*sin_lat2 - N - h);
+    local_ion_ellipsoid_a = (N+h) * sqrt(1.0 - local_ion_ellipsoid_e2*sin_lat2);
+    local_ion_ellipsoid_b = local_ion_ellipsoid_a*sqrt(1.0 - local_ion_ellipsoid_e2);
+
+    z_offset = ((1.0-earth_ellipsoid_e2)*N + h - (1.0-local_ion_ellipsoid_e2)*(N+h)) * sqrt(sin_lat2);
+
+    x1 = stationX/local_ion_ellipsoid_a;
+    y1 = stationY/local_ion_ellipsoid_a;
+    z1 = (stationZ-z_offset)/local_ion_ellipsoid_b;
+    c1 = x1*x1 + y1*y1 + z1*z1 - 1.0;
+
+    dx = direction[0] / local_ion_ellipsoid_a;
+    dy = direction[1] / local_ion_ellipsoid_a;
+    dz = direction[2] / local_ion_ellipsoid_b;
+    a = dx*dx + dy*dy + dz*dz;
+    b = x1*dx + y1*dy  + z1*dz;
+    alpha = (-b + sqrt(b*b - a*c1))/a;
+
+    pp_x = stationX + alpha*direction[0];
+    pp_y = stationY + alpha*direction[1]
+    pp_z = stationZ + alpha*direction[2];
+
+    normal_x = pp_x / (local_ion_ellipsoid_a * local_ion_ellipsoid_a);
+    normal_y = pp_y / (local_ion_ellipsoid_a * local_ion_ellipsoid_a);
+    normal_z = (pp_z-z_offset) / (local_ion_ellipsoid_b * local_ion_ellipsoid_b);
+
+    norm_normal2 = normal_x*normal_x + normal_y*normal_y + normal_z*normal_z;
+    norm_normal = sqrt(norm_normal2);
+    
+    pp_airmass = norm_normal / (direction[0]*normal_x + direction[1]*normal_y + direction[2]*normal_z);
+
+    return (np.array([[pp_x,pp_y,pp_z]]),pp_airmass)
+
+class PPdummy:
+    pass
+
+def getPiercePoints(time,source_positions,station_positions,height=450.e3):
+    me=measures()
+    piercepoints=PPdummy()
+    piercepoints.positions_xyz=np.zeros((source_positions.shape[0],station_positions.shape[0],3),dtype=np.float64)
+    piercepoints.positions=np.zeros((source_positions.shape[0],station_positions.shape[0],2),dtype=np.float64)
+    piercepoints.zenith_angles=np.zeros((source_positions.shape[0],station_positions.shape[0]),dtype=np.float64)
+    for isrc in range(source_positions.shape[0]) :
+        radec=source_positions[isrc]
+        for istat in range(station_positions.shape[0]):
+            stat_pos=station_positions[istat]
+            azel=radec2azel(radec[0],radec[1],time=str(time)+'s',pos=stat_pos)
+            az=azel['m0']['value'];
+            el=azel['m1']['value'];
+            lonlat=getLonLatStation(az,el,pos=stat_pos);
+
+            lon=lonlat['m0']['value'];
+            lat=lonlat['m1']['value'];
+
+            # convert to itrf coordinates on sphere with radius 1
+            diritrf=[cos(lat)*cos(lon),cos(lat)*sin(lon),sin(lat)]
+            (pp,am)=getPP(h=height,mPosition=stat_pos,direction=diritrf)
+            piercepoints.positions_xyz[isrc,istat]=np.array(pp[0])
+            piercepoints.zenith_angles[isrc,istat]=np.arccos(1./am)
+            pp1position=me.position("ITRF",str(pp[0,0])+'m',str(pp[0,1])+'m',str(pp[0,2])+'m')
+            # print "pp",  degrees(pp1position['m0']['value']),degrees(pp1position['m1']['value'])
+            piercepoints.positions[isrc,istat,0] = pp1position['m0']['value'];
+            piercepoints.positions[isrc,istat,1] = pp1position['m1']['value'];
+    return piercepoints
+
+def getLonLat(pos):
+    #converts ITRF pos in xyz to lon lat 
+    me=measures()
+    a=me.measure(me.position('ITRF',str(pos[0])+'m',str(pos[1])+'m',str(pos[2])+'m'),"ITRF");
+    return (a['m0']['value'],a['m1']['value'])
+
+def getLonLatStation(az=0,el=0,pos=posCS002):
+    #gets converts local station direction to ITRF lon/lat
+    if not isinstance(az,str):
+        az=str(az)+'rad';
+    if not isinstance(el,str):
+        el=str(el)+'rad';
+    me=measures()
+    me.do_frame(me.position('ITRF',str(pos[0])+'m',str(pos[1])+'m',str(pos[2])+'m'))
+    #me.do_frame(me.epoch('utc', 'today'))
+    direction=me.direction("AZEL",az,el)
+    return me.measure(direction,"ITRF");
+
+
+def radec2azel(ra,dec,time, pos):
+    me=measures();
+    if type(ra)!=str:
+        ra=str(ra)+'rad';
+    if type(dec)!=str:
+        dec=str(dec)+'rad';
+    phasedir=me.direction('J2000',ra,dec)
+    t=me.epoch("UTC",qa.quantity(time));
+    me.do_frame(t);
+
+    p = me.position('ITRF',str(pos[0])+'m',str(pos[1])+'m',str(pos[2])+'m')
+    me.do_frame(p);
+
+    azel = me.measure(phasedir,'azel');
+    return azel;
+
+def azel2radec(az,el,time, pos):
+    me=measures();
+    if type(az)!=str:
+        az=str(az)+'rad';
+    if type(el)!=str:
+        el=str(el)+'rad';
+    phasedir=me.direction('AZEL',az,el)
+    t=me.epoch("UTC",qa.quantity(time));
+    me.do_frame(t);
+
+    p = me.position('ITRF',str(pos[0])+'m',str(pos[1])+'m',str(pos[2])+'m')
+    me.do_frame(p);
+
+    radec = me.measure(phasedir,'RADEC');
+    return radec;
+
+
+
+def getStatPos(stations,AFPath='/opt/lofar/etc/StaticMetaData/',Field='LBA'):
+    StatPos=[];
+    for st in stations:
+        antFile=open(AFPath+st+"-AntennaField.conf")
+        line=antFile.readline()
+        while len(line)>0 and line[:len(Field)]!=Field:
+        
+            line=antFile.readline()
+        if line[:len(Field)]==Field:
+            StatPos.append([float(stp) for stp in antFile.readline().split()[2:5]])
+        else:
+            print("Field",Field,"for",st,"not found,putting zeros")
+            StatPos.append([0,0,0])
+        antFile.close()
+    return StatPos
diff --git a/CEP/Calibration/ExpIon/src/__init__.py b/CEP/Calibration/ExpIon/src/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..21bc2dc8a367fcbf4e108a15cbecd7bec1c375a3
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: iso-8859-1 -*-
+# __init__.py: Top level .py file for python solution analysis tools.
+#
+# Copyright (C) 2010
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: __init__.py 12729 2010-03-24 13:39:59Z vdtol $
+
+#from ionosphere import *
+
+__all__ = ['ionosphere', 'parmdbmain']
\ No newline at end of file
diff --git a/CEP/Calibration/ExpIon/src/acalc.py b/CEP/Calibration/ExpIon/src/acalc.py
new file mode 100644
index 0000000000000000000000000000000000000000..f888fc74daa919ee47f7d9f06a6842826ee62abc
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/acalc.py
@@ -0,0 +1,234 @@
+###############################################################################
+
+# import Python modules
+from math import pi, atan2, degrees, radians
+
+# import 3rd party modules
+from numpy import *
+
+# import user modules
+from .error import *
+try:
+  import _acalc
+except:
+  __acalc = False
+else:
+  __acalc = True
+
+###############################################################################
+# NOTE: there are several cases where one needs to preserve the sign of zero,
+# e.g. +0 and -0. 
+###############################################################################
+
+def factorial( n ):
+  nn = int( n )
+  if ( nn < 0 ):
+    fac = None
+  else:
+    if __acalc:
+      fac = _acalc.factorial( nn )
+    else:
+      fac = int( 1 )
+      while ( nn > 0 ):
+        fac = fac * int( nn )
+        nn = nn - 1
+  return fac
+
+###############################################################################
+
+def binomial( n, k ):
+  nn = int( n )
+  kk = int( k )
+  if ( kk < 0 ) or ( kk > nn ):
+    binom = None
+  else:
+    if __acalc:
+      binom = _acalc.binomial( nn, kk )
+    else:
+      binom = factorial( nn ) / ( factorial( kk ) * factorial( nn - kk ) )
+  return binom
+
+###############################################################################
+
+def complex_to_r_phi( c ):
+  if __acalc:
+    [ r, phi ] = _acalc.complex_to_r_phi( [ c.real, c.imag ] )
+  else:
+    r = abs( c )
+    phi = degrees( log( c ).imag )
+  return [ r, phi ]
+
+###############################################################################
+
+def r_phi_to_complex( rp ):
+  if __acalc:
+    [ cr, ci ] = _acalc.r_phi_to_complex( rp )
+    c = complex( cr, ci )
+  else:
+    [ r, phi ] = rp
+    c = r * exp( complex( 0., 1. ) * radians( phi ) )
+  return c
+
+###############################################################################
+
+def is_array( a ):
+  return isinstance( a, type( array( [ 1 ] ) ) )
+
+###############################################################################
+
+def azeros( x ):
+  if ( len( shape( x ) ) == 0 ):
+    zero = 0.
+  else:
+    zero = zeros( shape = x.shape, dtype = x.dtype )
+  return zero
+
+###############################################################################
+
+def aones( x ):
+  if ( len( x.shape ) == 0 ):
+    one = 1.
+  else:
+    one = ones( shape = x.shape, dtype = x.dtype )
+  return one
+
+###############################################################################
+
+def aatan2( y, x ):
+  if ( shape( x ) != shape( y ) ):
+    raise error( 'x and y have different shapes' )
+  if ( len( shape( x ) ) == 0 ):
+    z = atan2( y, x )
+  else:
+    xx = x.ravel()
+    yy = y.ravel()
+    zz = array( [ atan2( yy[ i ], xx[ i ] ) for i in range( len( xx ) ) ], dtype = x.dtype )
+    z = zz.reshape( x.shape )
+  return z
+
+###############################################################################
+def asign( x ):
+# this function also separates between -0 and +0
+  if ( not is_array( x ) ):
+    s = ( - 2. * float( aatan2( x, x ) < 0. ) + 1. )
+  else:
+    s = ( - 2. * array( aatan2( x, x ) < 0., dtype = x.dtype ) + 1. )
+  return s
+
+###############################################################################
+
+def amodulo( x, y ):
+  if ( not is_array( x ) ):
+    if ( not is_array( y ) ):
+      m = x - y * floor( x / ( y + float( y == 0. ) ) )
+    else:
+      xx = x * aones( y )
+      m = xx - y * floor( x / ( y + array( y == 0., dtype = y.dtype ) ) )
+  else:
+    if ( not is_array( y ) ):
+      yy = y * aones( x )
+      m = x - yy * floor( x / ( yy + array( yy == 0., dtype = yy.dtype ) ) )
+    else:
+      m = x - y * floor( x / ( y + array( y == 0., dtype = y.dtype ) ) )
+  return m
+
+###############################################################################
+
+def aradians( x ):
+  r = x * ( pi / 180. )
+  return r
+
+###############################################################################
+
+def adegrees( x ):
+  r = x * ( 180. / pi )
+  return r
+
+###############################################################################
+
+def awhere( a ):
+   return transpose( array( where( a ) ) )
+
+###############################################################################
+
+def aput( data, sel, sub ):
+# data, sel and sub must be arrays
+
+  # check input dimensions
+  if ( len( sel ) == 0 ):
+    return data.copy()
+  if ( len( sel.ravel() ) == 0 ):
+    return data.copy()
+  if ( sel.shape[ 1 ] > len( data.shape ) ):
+    raise error( 'number of dimensions of index array is higher than that of data array' )
+  asub = array( sub )
+  if ( len( asub.shape ) == len( data.shape ) - sel.shape[ 1 ] ):
+    if ( asub.shape != data.shape[ sel.shape[ 1 ] : ] ):
+      raise error( 'shape of subarray does not match selected data' )
+    asub = resize( asub, [ sel.shape[ 0 ] ] + list( data.shape[ sel.shape[ 1 ] : ] ) )
+  elif ( len( asub.shape ) == len( data.shape ) - sel.shape[ 1 ] + 1 ):
+    if ( list( asub.shape ) != [ sel.shape[ 0 ] ] + list( data.shape[ sel.shape[ 1 ] : ] ) ):
+      raise error( 'shape of subarray does not match selected data' )
+
+  # collapse and write data
+  coffset = [ int( product( data.shape[ i : sel.shape[ 1 ] ] ) ) for i in range( 1, 1 + sel.shape[ 1 ] ) ]
+  coffset[ - 1 ] = 1
+  csel = add.reduce( sel * array( coffset ), 1 )
+  subsize = int( product( data.shape[ sel.shape[ 1 ] : ] ) )
+  suboffset = resize( arange( subsize ), [ sel.shape[ 0 ], subsize ] )
+  csel = ( transpose( resize( csel * subsize, [ subsize, sel.shape[ 0 ] ] ) ) + suboffset ).ravel()
+  cdata = data.copy().ravel()
+  csub = asub.ravel()
+  put( cdata, csel, csub )
+
+  return cdata.reshape( data.shape )
+
+###############################################################################
+
+def aget( data, sel ):
+# data and sel must be arrays
+
+  # check input dimensions
+  if ( len( sel ) == 0 ):
+    return array( [], dtype = data.dtype )
+  if ( len( sel.ravel() ) == 0 ):
+    return array( [], dtype = data.dtype )
+  if ( sel.shape[ 1 ] > len( data.shape ) ):
+    raise error( 'number of dimensions of index array is higher than that of data array' )
+
+  # collapse data along sel axes
+  cdata_len = int( product( data.shape[ 0 : sel.shape[ 1 ] ] ) )
+  cdata_shape = [ cdata_len ] + list( data.shape[ sel.shape[ 1 ] : ] )
+  cdata = data.reshape( cdata_shape )
+  coffset = [ int( product( data.shape[ i : sel.shape[ 1 ] ] ) ) for i in range( 1, 1 + sel.shape[ 1 ] ) ]
+  coffset[ - 1 ] = 1
+  csel = add.reduce( sel * array( coffset ), 1 )
+
+  return take( cdata, csel, axis = 0 ) 
+
+###############################################################################
+
+def amean_phase( data ):
+  # determine phase average (with trick to get around possible phase wrap problems)
+  phases = array( [ data ], dtype = float64 ).ravel()
+  offsets = [ 0., 90., 180., 270. ]
+  mean_phases = [ ( amodulo( ( phases + offsets[ j ] ) + 180., 360. ) - 180. ).mean()
+      for j in range( len( offsets ) ) ]
+  var_phases = [ ( ( ( amodulo( ( phases + offsets[ j ] ) + 180., 360. ) - 180. ) - 
+      mean_phases[ j ] )**2 ).mean() for j in range( len( offsets ) ) ]
+  j = var_phases.index( min( var_phases ) )
+  mean_phase = mean_phases[ j ] - offsets[ j ]
+  return float( amodulo( mean_phase + 180, 360. ) - 180. )
+
+###############################################################################
+
+def amedian_phase( data ):
+  # determine phase average (with trick to get around possible phase wrap problems)
+  mean_phase = amean_phase( data )
+  phases = array( [ data ], dtype = float64 ).ravel()
+  median_phase = median( amodulo( ( phases - mean_phase ) + 180., 360. ) - 180. )
+  median_phase = amodulo( ( median_phase + mean_phase ) + 180., 360. ) - 180.
+  return float( median_phase )
+
+###############################################################################
+
diff --git a/CEP/Calibration/ExpIon/src/baselinefitting.cc b/CEP/Calibration/ExpIon/src/baselinefitting.cc
new file mode 100644
index 0000000000000000000000000000000000000000..792caea105a3dba5cf7bf79970070f53a0050db4
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/baselinefitting.cc
@@ -0,0 +1,228 @@
+//# fitting.cc: Clock and TEC fitting using cascore 
+//# 
+//#
+//# Copyright (C) 2012
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: $
+
+#include <lofar_config.h>
+#include <Common/OpenMP.h>
+
+#include <casacore/casa/Containers/ValueHolder.h>
+#include <casacore/casa/Containers/Record.h>
+#include <casacore/casa/Utilities/CountedPtr.h>
+#include <casacore/scimath/Fitting/LSQFit.h>
+
+#if defined(HAVE_CASACORE)
+#include <casacore/python/Converters/PycExcp.h>
+#include <casacore/python/Converters/PycBasicData.h>
+#include <casacore/python/Converters/PycValueHolder.h>
+#include <casacore/python/Converters/PycRecord.h>
+#define pyrap python
+#else
+#include <pyrap/Converters/PycExcp.h>
+#include <pyrap/Converters/PycBasicData.h>
+#include <pyrap/Converters/PycValueHolder.h>
+#include <pyrap/Converters/PycRecord.h>
+#endif
+
+#include <boost/python.hpp>
+#include <boost/python/args.hpp>
+
+using namespace casacore;
+using namespace boost::python;
+
+namespace LOFAR
+{
+namespace ExpIon
+{
+    
+ValueHolder fit(const ValueHolder &phases_vh, const ValueHolder &A_vh, const ValueHolder &init_vh, 
+                const ValueHolder &flags_vh, const ValueHolder &constant_parm_vh ) 
+{
+    // Arrays are passed as ValueHolder
+    // They are Array is extracted by the asArray<Type> methods
+    // They pointer to the actual data is obtained by the Array.data() method.
+    
+    // Get the phases 
+    Array<Float> phases = phases_vh.asArrayFloat();
+    Float *phases_data = phases.data();
+
+    // Get the flags
+    Array<Bool> flags(flags_vh.asArrayBool());
+    Bool noflags = (flags.ndim() == 0);
+    Bool *flags_data;
+    if (!noflags)
+    {
+        flags_data = flags.data();
+    }
+    
+    IPosition s = phases.shape();
+    int N_station = s[0];
+    int N_freq = s[1];
+    
+    // Get the matrix with basis functions
+    Array<Float> A = A_vh.asArrayFloat();
+    Float *A_data = A.data();
+    
+    IPosition s1 = A.shape();
+    
+    int N_coeff = s1[0];
+    int N_parm = N_station * N_coeff;
+    Float sol[N_parm];
+    
+    Array<Float> init(init_vh.asArrayFloat());
+    if (init.ndim() == 0)
+    {
+        for(int i = 0; i < N_parm; i++) sol[i] = 0.0;
+    }
+    else    
+    {
+        for(int i = 0; i < N_parm; i++) sol[i] = init.data()[i];
+    }
+    
+    // Get the flags indicating which parameters are constant_parm
+    // i.e. are not a free parameter in the minimization problem
+    Array<Bool> constant_parm(constant_parm_vh.asArrayBool());
+    Bool no_constant_parm = (constant_parm.ndim() == 0);
+    Bool *constant_parm_data;
+    if (!no_constant_parm)
+    {
+        constant_parm_data = constant_parm.data();
+    }
+    
+    Float cEq[N_parm];
+    for(int i=0; i<N_parm; ++i) cEq[i] = 0.0;
+    
+    int N_thread = OpenMP::maxThreads();
+    std::vector<LSQFit> lnl(N_thread);
+    
+    uInt nr = 0;
+    
+    for (int iter = 0; iter<1000; iter++)
+    {
+        for(int i = 0; i<N_thread; i++) {
+            lnl[i] = LSQFit(N_parm);
+        }
+        #pragma omp parallel
+        {
+            int threadNum = OpenMP::threadNum();
+            Float derivatives_re[2*N_coeff];
+            Float derivatives_im[2*N_coeff];
+            uInt idx[2*N_coeff];
+            #pragma omp for
+            for(int k = 0; k<N_freq; k++)
+            {
+                Float *A_data_k = A_data + k*N_coeff;
+                Float *phases_data_k = phases_data + k*N_station;
+                Bool *flags_data_k = flags_data + k*N_station;
+                for(int i = 1; i<N_station; i++) 
+                {
+                    Float phases_data_k_i = phases_data_k[i];
+                    Bool flags_data_k_i = flags_data_k[i];
+                    for(int j = 0; j<i; j++)
+                    {
+                        if (noflags || !(flags_data_k_i || flags_data_k[j]))
+                        {
+                            Float phase_ij_obs = phases_data_k_i - phases_data_k[j];
+                            Float phase_ij_model = 0.0;
+                            
+                            for (int l = 0; l<N_coeff; l++) 
+                            {
+                                Float coeff = A_data_k[l];
+                                phase_ij_model += (sol[i + l*N_station] - sol[j + l*N_station]) * coeff ;
+                            }
+                            Float sin_dphase, cos_dphase;
+#if defined(_LIBCPP_VERSION)
+#define sincosf __sincosf
+#endif
+                            sincosf(phase_ij_obs - phase_ij_model, &sin_dphase, &cos_dphase);
+                            Float residual_re = cos_dphase - 1.0;
+                            Float residual_im = sin_dphase;
+                            Float derivative_re = -sin_dphase;
+                            Float derivative_im = cos_dphase;
+ 
+                            
+                            for (int l = 0; l<N_coeff; l++)
+                            {
+                                Float coeff = A_data_k[l];
+                                Float a = derivative_re * coeff;
+                                Float b = derivative_im * coeff;
+                                derivatives_re[l] = a;
+                                derivatives_re[N_coeff + l] = -a;
+                                derivatives_im[l] = b;
+                                derivatives_im[N_coeff + l] = -b;
+                                idx[l] = i+l*N_station;
+                                idx[N_coeff + l] = j+l*N_station;
+                            }
+                            lnl[threadNum].makeNorm(uInt(2*N_coeff), (uInt*) idx, (Float*) derivatives_re, Float(1.0), residual_re);
+                            lnl[threadNum].makeNorm(uInt(2*N_coeff), (uInt*) idx, (Float*) derivatives_im, Float(1.0), residual_im);
+                        }
+                    }
+                }
+            }
+        }
+        
+        for(int i = 1; i<N_thread; i++)
+        {
+            lnl[0].merge(lnl[i]);
+        }
+        
+        if ((!no_constant_parm) )
+        {
+            for (int i = 0; i<N_parm; i++) 
+            {
+                if (constant_parm_data[i])
+                {
+                    cEq[i] = 1.0;
+                    lnl[0].addConstraint( (Float*) cEq, 0.0);
+                    cEq[i] = 0.0;
+                }
+            }
+        }
+        
+        if (!lnl[0].solveLoop(nr, sol, True)) 
+        {
+            cout << "Error in loop: " << nr << endl;
+            break;
+        }
+        if (lnl[0].isReady())
+        {
+            break;
+        }
+    }
+    
+    Array<Float> solutions(IPosition(2, N_station, N_coeff), sol);
+    ValueHolder result(solutions);
+    return result;
+};
+
+
+} // namespace ExpIon
+} // namespace LOFAR
+
+BOOST_PYTHON_MODULE(_baselinefitting)
+{
+    casacore::pyrap::register_convert_excp();
+    casacore::pyrap::register_convert_basicdata();
+    casacore::pyrap::register_convert_casa_valueholder();
+    casacore::pyrap::register_convert_casa_record();
+    
+    def("fit", LOFAR::ExpIon::fit);
+}
diff --git a/CEP/Calibration/ExpIon/src/baselinefitting.py b/CEP/Calibration/ExpIon/src/baselinefitting.py
new file mode 100644
index 0000000000000000000000000000000000000000..4708eea126511836feeeec40920344c173cebd05
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/baselinefitting.py
@@ -0,0 +1,35 @@
+"""Fit basefunctions to phases using all baselines
+
+Application: Clock-TEC separation
+
+
+The phase can be described by a linear model
+
+.. math::
+   phases = A p
+   
+where the columns of matrix A contain the basefunctions and p are the parameters
+
+The same equation holds for the phases of multiple stations
+The dimension of phases is then N_freq x N_station and 
+the dimension of p is N_freq x N_param
+
+The cost function that will be minimized is 
+
+.. math::
+   \\sum_{i=1}^{N_{station}-1}\\sum_{j=0}^{i-1} \\sum_{k=0}^{N_{freq}} \\| \\exp(\imath(\\Delta phase_{ijk} - \\Delta modelphase_{ijk}))  \\|^2
+where
+.. math::
+   \\Delta phase_{ijk} =
+   \\Delta modelphase_{ijk} = 
+
+
+
+"""
+  
+ 
+import _baselinefitting
+ 
+def fit(phases, A, p_0 = None, flags = None, constant_parms = None):
+    """see module description for detailed info"""
+    return _baselinefitting.fit(phases, A, p_0, flags, constant_parms)
\ No newline at end of file
diff --git a/CEP/Calibration/ExpIon/src/calibrate-ion b/CEP/Calibration/ExpIon/src/calibrate-ion
new file mode 100755
index 0000000000000000000000000000000000000000..6811a1d0e0fa6d9c0fb9a21d5cde40df95cbacb5
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/calibrate-ion
@@ -0,0 +1,231 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2007
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id$
+
+import sys
+import os
+import re
+import lofar.expion.ionosphere as ionosphere
+import lofar.parameterset
+
+from pylab import *
+
+arguments = sys.argv.__iter__()
+scriptname = os.path.basename(arguments.next())
+
+def print_error(msg):
+   print "%s: %s" % (scriptname, msg)
+
+sky_name = 'sky'
+instrument_name = 'instrument'
+clusterdescfile = "~/CEP.clusterdesc"
+globaldb = 'globaldb'
+
+def display_help_and_exit(dummy = None):
+   print '''usage:
+   %s [options] <parsetfile> <gdsfiles> ''' % scriptname
+   print
+   print '''arguments:
+   <gdsfiles>     gds file(s)'''
+   print
+   print '''options:
+   -h, --help        display this help text
+   --cluster-desc <cluster description file>
+                     Define cluster description file
+                     (default: ~/CEP.clusterdesc)
+   --sky-name <skyname>
+                     Define basename for the sky model parmdb
+                     (default: sky)
+   --instrument-name <instrumentname>
+                     Define basename of the instrument parmdb
+                     (default: instrument)
+   '''
+   exit()
+
+def set_clusterdesc(arguments):
+   global clusterdescfile
+   try:
+      clusterdescfile = arguments.next()
+      if clusterdescfile[0] == '-':
+         raise ValueError
+   except (KeyError, ValueError):
+      print_error( "--cluster-desc should be followed by the name of the cluster description file")
+      exit()
+
+def set_instrument_name(arguments):
+   global instrument_name
+   try:
+      instrument_name = arguments.next()
+      if instrument_name[0] == '-':
+         raise ValueError
+   except (KeyError, ValueError):
+      print_error( "--instrument-name should be followed by the basename of the instrument parmdb")
+      exit()
+
+def set_sky_name(arguments):
+   global sky_name
+   try:
+      sky_name = arguments.next()
+      if sky_name[0] == '-':
+         raise ValueError
+   except (KeyError, ValueError):
+      print_error( "--sky-name should be followed by the basename of the sky parmdb")
+      exit()
+
+def set_globaldb(arguments):
+   global globaldb
+   try:
+      globaldb = arguments.next()
+      if globaldb[0] == '-':
+         raise ValueError
+   except (KeyError, ValueError):
+      print_error( "--globaldb should be followed by the global")
+      exit()
+
+options = { '-h':                display_help_and_exit,
+            '--help':            display_help_and_exit,
+            '--cluster-desc':    set_clusterdesc,
+            '--sky':             set_sky_name,
+            '--instrument-name': set_instrument_name,
+            '--globaldb':              set_globaldb}
+
+while True:
+   try: 
+      argument = arguments.next()
+   except StopIteration:
+      print_error( "No parset file and no gds file(s) specified" )
+      display_help_and_exit()
+   if argument[0] == '-':
+      try:
+         options[argument](arguments)
+      except KeyError:
+         print_error( "Unknown option: " + argument )
+         display_help_and_exit()
+   else:
+      break
+
+parsetfile = argument
+parset = lofar.parameterset.parameterset( parsetfile )
+
+print clusterdescfile
+clusterdescfile = os.path.expanduser( clusterdescfile )
+print clusterdescfile
+
+clusterdesc = lofar.parameterset.parameterset( clusterdescfile )
+   
+gdsfiles = []
+gdsfiles.extend(arguments)
+
+if len(gdsfiles) == 0 :
+   print_error( "No gds file(s) or globaldb specified" )
+   display_help_and_exit()
+
+
+print "parset-file: " + repr(parsetfile)
+print "gds-files: " + repr(gdsfiles)
+print "instrument-name: " + repr(instrument_name)
+print "sky-name: " + repr(sky_name)
+
+stations = parset.getStringVector("ExpIon.Stations", [])
+sources = parset.getStringVector("ExpIon.Sources", [])
+
+d = {'XX' : 0, 'YY' : 1}
+l = parset.getStringVector("ExpIon.Polarizations", ['XX', 'YY'])
+polarizations = [d[key] for key in l]
+
+DirectionalGainEnable = parset.getBool( "ExpIon.DirectionalGain.Enable", False ) 
+GainEnable = parset.getBool( "ExpIon.Gain.Enable", False ) 
+PhasorsEnable = parset.getBool( "ExpIon.Phasors.Enable", False )
+RotationEnable = parset.getBool( "ExpIon.Rotation.Enable", False )
+print "RotationEnable:", RotationEnable
+
+print repr(stations)
+print repr(sources)
+print repr(DirectionalGainEnable)
+
+ion_model = ionosphere.IonosphericModel(gdsfiles, clusterdescfile,
+                                        GainEnable = GainEnable,
+                                        DirectionalGainEnable = DirectionalGainEnable,
+                                        RotationEnable = RotationEnable,
+                                        PhasorsEnable = PhasorsEnable,
+                                        stations = stations,
+                                        sources = sources,
+                                        polarizations = polarizations,
+                                        instrument_name = instrument_name,
+                                        globaldb = globaldb)
+
+def operation_clocktec ( step ):
+   ClockEnable = parset.getBool('.'.join(["ExpIon.Steps", step, "Clock.Enable"]), True )
+   TECEnable = parset.getBool('.'.join(["ExpIon.Steps", step, "TEC.Enable"]), True )
+   print '.'.join(["ExpIon.Steps", step, "Stations"])
+   stations = parset.getStringVector('.'.join(["ExpIon.Steps", step, "Stations"]), [])
+   print "stations: ", stations
+   if ClockEnable or TECEnable :
+      ion_model.ClockTEC( ClockEnable = ClockEnable, TECEnable = TECEnable, stations = stations )
+   
+
+def operation_fitmodel( step ) :
+   height = parset.getFloat('.'.join(["ExpIon.Steps", step, "height"]), 200.0e3 )
+   ion_model.calculate_piercepoints(height = height)
+
+   order = parset.getInt('.'.join(["ExpIon.Steps", step, "order"]), 2 )
+   ion_model.calculate_basevectors( order = order )
+
+   ion_model.fit_model()
+
+def operation_makemovie( step ) :
+   npixels = parset.getInt('.'.join(["ExpIon.Steps", step, "npixels"]), 100 )
+   extent = parset.getFloat('.'.join(["ExpIon.Steps", step, "extent"]), 0 )
+   clim = parset.getFloatVector('.'.join(["ExpIon.Steps", step, "clim"]), [] )
+   if len(clim) == 2:
+      vmin = clim[0]
+      vmax = clim[1]
+   else:
+      vmin = 0
+      vmax = 0
+   ion_model.make_movie(extent = extent, npixels = npixels, vmin = vmin, vmax = vmax)
+
+def operation_store ( step ):
+   ClockTEC_parmdbname = parset.getString('.'.join(["ExpIon.Steps", step, "ClockTEC.parmdb"]), "ClockTEC.parmdb")
+   phases_parmdbname = parset.getString('.'.join(["ExpIon.Steps", step, "Phases.parmdb"]), "ionosphere")
+   ion_model.write_to_parmdb(  )
+   #ion_model.write_phases_to_parmdb( ClockTEC_parmdbname, phases_parmdbname )
+   
+def operation_plot ( step ):
+   print ion_model.TEC.shape
+   figure(1)
+   plot(ion_model.Clock[0,:,:])
+   figure(2)
+   plot(ion_model.TEC[0,:,:])
+   show()
+
+Operations = { "CLOCKTEC": operation_clocktec ,
+               "FITMODEL": operation_fitmodel,
+               "MAKEMOVIE": operation_makemovie,
+               "STORE":  operation_store,
+               "PLOT":  operation_plot }
+
+steps = parset.getStringVector("ExpIon.Steps", [] )
+
+for step in steps:
+   operation = parset.getString( '.'.join( [ "ExpIon.Steps", step, "Operation" ] ) )
+   Operations[ operation ] ( step )
+
diff --git a/CEP/Calibration/ExpIon/src/client.py b/CEP/Calibration/ExpIon/src/client.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d94ba48702e20f23a3757178ceaebe7b4529900
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/client.py
@@ -0,0 +1,41 @@
+ # Compatability layer for IPython.client 0.10 and IPython.parallel >= 0.11
+
+import IPython
+
+if [int(v) for v in IPython.__version__.split('.')] < [0,11] :
+  from IPython.kernel import client
+  import atexit
+# Without the following statement python sometimes throws an exception on exit
+  atexit.register(client.rit.stop)
+  MultiEngineClient = client.MultiEngineClient
+  TaskClient = client.TaskClient
+  MapTask = client.MapTask
+else:
+  from IPython.parallel import Client
+  
+  def MultiEngineClient() :
+    rc = Client()
+    dview = rc[:]
+    return dview
+    
+  class TaskClient :
+    def __init__(self) :
+      self.rc = Client()
+      self.dview = self.rc[:]
+      self.lbview = self.rc.load_balanced_view()
+    
+    def run(self, maptask) :
+      return self.lbview.apply(maptask.func, *maptask.args)
+      
+    def get_task_result(self, task, block = True) :
+      return task.get()
+    
+    def clear(self):
+      pass
+    
+  class MapTask :
+    def __init__(self, func, args) :
+      self.func = func
+      self.args = args
+      pass
+    
\ No newline at end of file
diff --git a/CEP/Calibration/ExpIon/src/error.py b/CEP/Calibration/ExpIon/src/error.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e9ec7edc9e6d2e6651f59600d927dab129eebb7
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/error.py
@@ -0,0 +1,6 @@
+###############################################################################
+
+def error( string ):
+  raise RuntimeError( string )
+
+###############################################################################
diff --git a/CEP/Calibration/ExpIon/src/fitClockTEC.py b/CEP/Calibration/ExpIon/src/fitClockTEC.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b753e87c4d5ce8c17b3333f9bb4522fb2494249
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/fitClockTEC.py
@@ -0,0 +1,938 @@
+import numpy as np;
+import lofar.expion.parmdbmain as parmdbmain
+import lofar.expion.baselinefitting as fitting
+#import lofar.expion.fitting as fitting
+from scipy import optimize as opt
+import tables as tab
+import sys
+#from pylab import *
+from numpy import *
+light_speed=299792458.
+
+clockarray=0
+tecarray=0
+offsetarray=0
+residualarray=0
+
+
+def ClockTECfunc(xarray,par):
+    delay=np.array([-1*par[1]*1e-9]).flatten() #in ns, array has dimension 1, even if scalar
+    delayfact=2*np.pi*delay[:,np.newaxis]*xarray
+    TEC=np.array([par[0]]).flatten();          # dTEC in TECU
+    drefract=-8.4479745e9*TEC[:,np.newaxis]/xarray;
+    return drefract[:,np.newaxis,:]+delayfact+par[2]; #returns nTEC x nClock x nFreq
+
+def getRM(ion,station,refStationIdx=0,starttime=0,SBselect='all'):
+    '''get Rotation Measure vs. time and residual rotation'''
+    freqs=ion.freqs[:]
+    nF=freqs.shape[0]
+    if SBselect=='all':
+        SBselect=np.ones(nF,dtype=bool)
+    if isinstance(SBselect,list) and len(SBselect)==2:
+        SBselect=np.logical_and(freqs>SBselect[0],freqs<SBselect[1])
+    ant1 = list(ion.stations[:]).index(station)
+    ant2 = refStationIdx
+    A = (light_speed/ freqs[SBselect])**2
+    A = np.reshape(A, (-1,1))
+    rot=ion.rotation[starttime:,:,ant1][:,SBselect]-ion.rotation[starttime:,:,ant2][:,SBselect]
+    #remove nans
+    rot[np.isnan(rot)]=0.
+    while len(rot.shape)>2:
+        rot=rot[:,:,0]
+    
+    RM=np.remainder(rot+np.pi,2*np.pi)-np.pi
+    
+    RMtime=  np.dot(1./(np.dot(A.T,A)), np.dot(A.T,RM.T)).flatten()
+    residuals=RM-RMtime[:,np.newaxis]*A.T
+    return RMtime,residuals
+
+def getInitPar(data,dTECArray, dClockArray,freqs,ff=ClockTECfunc):
+    '''initialize paramaters and unwraps data for fit'''
+    if np.min(np.absolute(dTECArray))<1e-5:
+        dTECArray+=0.0001 #just to prevent 0 to be there, since otherwise the fit might get stuck in 0 
+    nT=dTECArray.shape[0]
+    nD=dClockArray.shape[0]
+    par=[dTECArray,dClockArray,0]
+    # first check all unwrapping possibilities
+    bigdata=ff(freqs,par) # returns array of shape nT,nD,nF
+    wraps=np.around(np.divide(bigdata-data,2*np.pi));
+    difference=  bigdata-data-wraps*2*np.pi
+    offset=np.average(difference,axis=2);
+    index=np.unravel_index(
+        np.argmin(
+            np.sum(np.absolute((difference.T-offset.T).T),axis=2))
+        ,(nT,nD));
+    OffsetIn=-1*offset[index];
+    par=[dTECArray[index[0]],dClockArray[index[1]],OffsetIn];
+    estimate=ff(freqs,par).flatten()
+    wraps=np.around(np.divide(estimate-data,2*np.pi));
+    data[:]=np.add(2*np.pi*wraps,data)
+    return par
+
+def getClockTECAll(ph,amp,freqs,stationname,stIdx,polIdx):
+    global tecarray
+    global clockarray
+    global offsetarray
+
+    maxTimesteps=500
+    #first unwrap data and get initial guess
+    nT=ph[:].shape[0]
+    nF=freqs[:].shape[0]
+
+    stepDelay=.03
+    if 'CS' in stationname:
+        iTEC1=-0.1
+        iTEC2=0.1
+        iD1=-2
+        iD2=2
+    else:
+        iTEC1=-0.4
+        iTEC2=0.4
+        iD1=-200
+        iD2=200
+
+    if "HBA" in stationname:
+        stepdTEC=0.0005
+    else:
+        stepdTEC=0.0001  # faster wrapping for LBA 
+
+    tmsteps=nT/maxTimesteps
+    for istep in range(tmsteps):
+     bigshape=[0,2*min(maxTimesteps,nT-maxTimesteps*istep)+1]
+     alldata=np.array([])
+     allfreqs=[]
+     for itm in range(istep*maxTimesteps,min(nT,(istep+1)*maxTimesteps)):
+        
+        if itm%100==0 and itm>0:
+            sys.stdout.write(str(itm)+'... '+str(par[0])+' '+str(par[1])+' '+str(par[2])+' ')
+            sys.stdout.flush()
+        if itm>0:
+            iTEC1=par[0]-0.1
+            iTEC2=par[0]+0.1
+            iD1=par[1]-10
+            iD2=par[1]+10
+        dTECArray=np.arange(iTEC1,iTEC2,stepdTEC)
+        dClockArray=np.arange(iD1,iD2,stepDelay)
+
+        flags=(amp[itm,:]!=1);
+        nrFlags=np.sum(np.logical_not(flags))
+        data=ph[itm,:][flags]
+        tmpfreqs=freqs[flags]
+        par = getInitPar(data,dTECArray, dClockArray,tmpfreqs,ClockTECfunc,useOffset=False,plot_debug=(itm%100==0))
+
+        alldata=np.append(alldata,data)
+        bigshape[0]+=tmpfreqs.shape[0]
+        allfreqs.append(tmpfreqs)
+        
+        
+     print("got bigmatrix",bigshape)
+     bigmatrix=np.zeros(bigshape)
+     idx=0
+     
+     for itm in range(min(nT-istep*maxTimesteps,maxTimesteps)):
+        nextidx=allfreqs[itm].shape[0]
+        bigmatrix[idx:idx+nextidx,2*itm]=-8.4479745e9/allfreqs[itm]
+        bigmatrix[idx:idx+nextidx,2*itm+1]=-2.e-9*np.pi*allfreqs[itm]
+        idx+=nextidx
+     bigmatrix[:,-1]+=1
+     print("fitting",bigmatrix.shape,alldata.shape)
+     sol=np.linalg.lstsq(bigmatrix,alldata)
+     finalpar=sol[0]
+     print("result",finalpar[0],finalpar[1],finalpar[-1])
+     offsetarray[istep*maxTimesteps:(istep+1)*maxTimesteps,stIdx,polIdx]=finalpar[-1]
+     tecarray[istep*maxTimesteps:(istep+1)*maxTimesteps,stIdx,polIdx]=finalpar[:-1].reshape(-1,2)[:,0]
+     clockarray[istep*maxTimesteps:(istep+1)*maxTimesteps,stIdx,polIdx]=finalpar[:-1].reshape(-1,2)[:,1]
+
+def getClockTEC(ph,amp,freqs,SBselect,stationname,stIdx,polIdx,fixedOffset=False):
+    global tecarray
+    global clockarray
+    global offsetarray
+    global residualarray
+
+    errorf= lambda par,x,data:  (-1*par[0]*x*2*np.pi*1e-9+par[1]-data).flatten() #delay function+ offset
+    #remove nans
+    ph[np.isnan(ph)]=0.
+    ph=np.unwrap(np.remainder(ph,2*np.pi)) # unwrap last (=freq) axis
+    par=[0.,0.]
+    avg_delay=0.
+    if ("CS" in stationname) or ("HBA" in stationname):
+        #do not do this step for RS stations LBA,because ionospheric fluctuations
+        result=opt.leastsq(errorf,par,args=(freqs,ph)) #get average delay
+        avg_delay=result[0][0]
+
+    print("avg_delay",stationname,polIdx,avg_delay)
+    # define the function we want to fit, for core stations keep delay fixed
+    stepDelay=.3
+    if 'CS' in stationname:
+        fixedDelay=True
+        if fixedOffset:
+            ff=lambda x,par:ClockTECfunc(x,[par[0],avg_delay,0.])
+        else:
+            ff=lambda x,par:ClockTECfunc(x,[par[0],avg_delay,par[1]])
+        initTEC1=-0.5
+        initTEC2=0.5
+        initD1=avg_delay
+        initD2=avg_delay+stepDelay
+    else:
+        fixedDelay=False
+        if fixedOffset:
+            ff=lambda x,par:ClockTECfunc(x,[par[0],par[1],0.])
+        else:
+            ff=lambda x,par:ClockTECfunc(x,par)
+        par=[0.,0.,0.]
+        initTEC1=-2
+        initTEC2=2
+        if "HBA" in stationname:
+            initD1=avg_delay-30
+            initD2=avg_delay+30
+        else:
+            initD1=-200
+            initD2=200
+
+    if "HBA" in stationname:
+        stepdTEC=0.01
+    else:
+        stepdTEC=0.008  # faster wrapping for LBA 
+
+    errorf = lambda par,x,y: (ff(x,par)-y).flatten()
+
+    nTimes=ph.shape[0]
+    nF=ph.shape[1]
+
+    success=False
+    iTEC1=initTEC1
+    iTEC2=initTEC2
+    iD1=initD1
+    iD2=initD2
+    finalpar=[0]*nTimes
+    print(stationname,polIdx,"tm:", end=' ')
+    for tm in range(0,nTimes):
+        if tm%100==0:
+            sys.stdout.write(str(tm)+'...')
+            sys.stdout.flush()
+        if tm>0 and success:
+            iTEC1=finalpar[tm-1][0]-5*stepdTEC
+            iTEC2=finalpar[tm-1][0]+6*stepdTEC
+            if not fixedDelay:
+                iD1=finalpar[tm-1][1]-1*stepDelay
+                iD2=finalpar[tm-1][1]+2*stepDelay
+        else:
+            iTEC1=max(iTEC1-5*stepdTEC,min(initTEC1,iTEC2-5))
+            iTEC2=min(iTEC2+6*stepdTEC,max(initTEC2,iTEC2+5))
+            if not fixedDelay:
+                iD1=max(iD1-1*stepDelay,min(initD1,iD2-60))
+                iD2=min(iD2+2*stepDelay,max(initD2,iD1+60))
+        itm=tm
+        dTECArray=np.arange(iTEC1,iTEC2,stepdTEC)
+        dClockArray=np.arange(iD1,iD2,stepDelay)
+
+        flags=(amp[itm,:]!=1);
+        nrFlags=np.sum(np.logical_not(flags))
+        data=ph[itm,:][flags]
+        tmpfreqs=freqs[flags]
+        
+        if nrFlags>0.5*nF:
+            print("TOO many data points flagged:",tm,tmpfreqs.shape[0],"remaining")
+            if tm>0:
+                finalpar[tm]=np.array(finalpar[tm-1])
+            else:
+                finalpar[tm]=np.array(par)
+            success=False
+            continue
+        par = getInitPar(data,dTECArray, dClockArray,tmpfreqs,ClockTECfunc)
+        if fixedDelay:
+            par=par[:1]+par[2:]
+        if fixedOffset:
+            par=par[:-1]
+        (finalpar[tm],success)=opt.leastsq(errorf,par,args=(tmpfreqs,data))
+        #print "fitted",tm,(finalpar[tm],success)
+        if not hasattr(finalpar[tm],'__len__'):
+            finalpar[tm]=[finalpar[tm]]
+        chi2 = np.average(np.power(errorf(par,tmpfreqs,data), 2))
+        if chi2>10:
+            print("got a Fail",stationname,itm,chi2,finalpar[tm])
+            success=False
+        else:
+            residualarray[itm,SBselect,stIdx,polIdx][flags]=errorf(finalpar[tm],tmpfreqs,data)
+         
+    print('finished')
+    #acquire lock?, store data
+    finalpar=np.array(finalpar)
+    tecarray[:,stIdx,polIdx]=np.array(finalpar)[:,0]
+    if fixedDelay:
+        clockarray[:,stIdx,polIdx]=avg_delay*np.ones(clockarray.shape[0])
+        if not fixedOffset:
+            offsetarray[:,stIdx,polIdx]=np.array(finalpar)[:,1]
+    else:
+        clockarray[:,stIdx,polIdx]=np.array(finalpar)[:,1]
+        if not fixedOffset:
+            offsetarray[:,stIdx,polIdx]=np.array(finalpar)[:,2]
+
+
+def getResidualPhaseWraps(avgResiduals,freqs):
+    flags=avgResiduals!=0.
+    nSt=avgResiduals.shape[1]
+    nF=freqs.shape[0]
+    wraps=np.zeros((nSt,),dtype=np.float)
+    for ist in range(nSt):
+        print(ist)
+        tmpfreqs=freqs[flags[:,ist]]
+        nF=tmpfreqs.shape[0]
+        if nF<10:
+            print("too many flagged",ist)
+            continue
+        basef,steps=getPhaseWrapBase(tmpfreqs)
+
+        data=avgResiduals[flags[:,ist],ist]
+        wraps[ist]=np.dot(1./(np.dot(basef.T,basef)), np.dot(basef,data))
+    return wraps,steps
+
+def getPhaseWrapBase(freqs):
+    nF=freqs.shape[0]
+    A=np.zeros((nF,2),dtype=np.float)
+    A[:,1] = freqs*2*np.pi*(-1e-9)
+    A[:,0] = -8.44797245e9/freqs
+    steps=np.dot(np.dot(np.linalg.inv(np.dot(A.T,A)),A.T),2*np.pi*np.ones((nF,),dtype=np.float))
+    basef=np.dot(A,steps)-2*np.pi
+    return basef,steps
+
+def getResidualPhaseWraps2(avgResiduals,freqs):
+    flags=avgResiduals[:,10]==0.
+    nSt=avgResiduals.shape[1]
+    nF=freqs.shape[0]
+    wraps=np.zeros((nSt,),dtype=np.float)
+    #tmpflags=np.sum(flags[:,np.sum(flags,axis=0)<(nF*0.5)],axis=1)
+    tmpflags=flags
+    tmpfreqs=freqs[np.logical_not(tmpflags)]
+    tmpbasef,steps=getPhaseWrapBase(tmpfreqs)
+    basef=np.zeros(freqs.shape)
+    basef[np.logical_not(tmpflags)]=tmpbasef
+    basef=basef.reshape((-1,1))
+    
+    data=avgResiduals[:,:]
+        
+
+    wraps=fitting.fit(data,basef,wraps,flags).flatten()
+    return wraps,steps
+
+
+def getTECBaselineFit(ph,amp,freqs,SBselect,polIdx,stIdx,useOffset=False,stations=[],initSol=[],chi2cut=300.,timeIdx=0):
+    global tecarray
+    global offsetarray
+    global residualarray
+    amp[np.isnan(ph)]=1
+    ph[np.isnan(ph)]=0.
+    ph=np.unwrap(ph,axis=0) 
+    #first unwrap data and get initial guess
+    nT=ph.shape[0]
+    nF=freqs.shape[0]
+    nSt=ph.shape[2]
+    nparms=1+(useOffset>0)
+    sol = np.zeros((nSt,nparms),dtype=np.float)
+    A=np.zeros((nF,nparms),dtype=np.float)
+    A[:,0] = -8.44797245e9/freqs
+    if useOffset:
+        A[:,1] = np.ones((nF,))
+    # init first sol
+    big_array=(np.arange(-0.1,0.1,0.005)*A[:,0][:,np.newaxis]).T
+    diff=np.sum(np.absolute(np.remainder(big_array[:,:,np.newaxis]-(ph[0,:,:]-ph[0,:,:][:,[0]])+np.pi,2*np.pi)-np.pi),axis=1)
+    init_idx=np.argmin(diff,axis=0)
+    sol[:,0]=init_idx*0.005-0.1
+    print("Initializing with",sol[:,0])
+    for itm in range(nT):
+        
+        if itm%100==0 and itm>0:
+            sys.stdout.write(str(itm)+'... '+str(sol[-1,0]-sol[0,0])+' '+str(sol[-1,-1]-sol[0,-1])+' ')
+            sys.stdout.flush()
+        flags=(amp[itm,:]==1);
+        nrFlags=np.sum(flags,axis=0)
+        sol=fitting.fit(ph[itm],A,sol.T,flags).T
+        tecarray[itm+timeIdx,stIdx,polIdx]=sol[:,0]
+        if useOffset:
+            offsetarray[itm+timeIdx,stIdx,polIdx]=sol[:,1]
+        residual = ph[itm] - np.dot(A, sol.T)
+        residual = residual - residual[:, 0][:,np.newaxis]
+        residual = np.remainder(residual+np.pi, 2*np.pi) - np.pi       
+        residual[flags]=0
+        residualarray[np.ix_([itm+timeIdx],SBselect,stIdx,[polIdx])]=residual.reshape((1,nF,nSt,1))
+
+        
+def getClockTECBaselineFit(ph,amp,freqs,SBselect,polIdx,stIdx,useOffset=False,stations=[],initSol=[],chi2cut=300.,fixedClockforCS=False,timeIdx=0):
+    global tecarray
+    global clockarray
+    global offsetarray
+    global residualarray
+    amp[np.isnan(ph)]=1
+    ph[np.isnan(ph)]=0.
+    ph=np.unwrap(ph,axis=0) 
+    #first unwrap data and get initial guess
+    nT=ph.shape[0]
+    nF=freqs.shape[0]
+    nSt=ph.shape[2]
+    nparms=2+(useOffset>0)
+    #sol = np.zeros((nSt,nparms),dtype=np.float)
+    sol = np.zeros((nSt,nparms),dtype=np.float)
+    print(sol.shape,nparms,nSt)
+    A=np.zeros((nF,nparms),dtype=np.float)
+    A[:,1] = freqs*2*np.pi*(-1e-9)
+    A[:,0] = -8.44797245e9/freqs
+    if useOffset:
+        A[:,2] = np.ones((nF,))
+
+    constant_parms=np.zeros(sol.shape,dtype=bool)
+    if fixedClockforCS:
+        for ist,st in enumerate(stations):
+            if 'CS' in st:
+                constant_parms[ist,1]=True
+    stepDelay=1
+
+    if "HBA" in stations[0]:
+        stepdTEC=0.005
+    else:
+        stepdTEC=0.001  # faster wrapping for LBA 
+        stepDelay=3
+ 
+    succes=False
+    initprevsol=False
+    nrFail=0
+    for itm in range(nT):
+        
+        if itm%100==0 and itm>0:
+            sys.stdout.write(str(itm)+'... '+str(sol[-1,0]-sol[0,0])+' '+str(sol[-1,1]-sol[0,1])+' '+str(sol[-1,-1]-sol[0,-1])+' ')
+            sys.stdout.flush()
+ 
+        flags=(amp[itm,:]==1);
+        nrFlags=np.sum(flags,axis=0)
+        if itm==0 or not succes:
+         for ist in range(1,nSt):
+            if (nF-nrFlags[ist])<10:
+                print("Too many data points flagged",itm,ist)
+                continue;
+            if itm==0 or not initprevsol:
+                if hasattr(initSol,'__len__') and len(initSol)>ist:
+                    iTEC1=initSol[ist,0]
+                    iTEC2=initSol[ist,0]+stepdTEC
+                    iD1=initSol[ist,1]
+                    iD2=initSol[ist,1]+stepDelay
+                else:
+                 if 'CS' in stations[ist]:
+                    iTEC1=-0.2
+                    iTEC2=0.2
+                    iD1=-4
+                    iD2=4
+                 else:
+                    iTEC1=-1.5
+                    iTEC2=1.5
+                    iD1=-50
+                    iD2=300
+                print("First",iTEC1,iTEC2,iD1,iD2)
+                    
+
+            else:
+                
+                iTEC1=prevsol[ist,0]-stepdTEC*nrFail
+                iTEC2=prevsol[ist,0]+stepdTEC*(nrFail+1)
+                if not fixedClockforCS or not 'CS' in stations[ist]: 
+                    iD1=prevsol[ist,1]-stepDelay*nrFail
+                    iD2=prevsol[ist,1]+stepDelay*(nrFail+1)
+                else:
+                    iD1=sol[ist,1]
+                    iD2=sol[ist,1]+stepDelay
+                    
+                print("Failure",iTEC1,iTEC2,iD1,iD2,nrFail)
+
+            dTECArray=np.arange(iTEC1,iTEC2,stepdTEC)
+            dClockArray=np.arange(iD1,iD2,stepDelay)
+            data=ph[itm,:,ist][np.logical_not(np.logical_or(flags[:,ist],flags[:,0]))]-ph[itm,:,0][np.logical_not(np.logical_or(flags[:,ist],flags[:,0]))]
+            tmpfreqs=freqs[np.logical_not(np.logical_or(flags[:,ist],flags[:,0]))]
+            print("getting init",ist, end=' ')
+            par = getInitPar(data,dTECArray, dClockArray,tmpfreqs,ClockTECfunc)
+            print(par)
+            sol[ist,:]=par[:nparms]
+        if not succes:
+            #reset first station
+            sol[0,:] = np.zeros(nparms)
+        #sol=fitting.fit(ph[itm],A,sol.T,flags).T
+        #sol=fitting.fit(ph[itm],A,sol,flags)
+        sol=fitting.fit(ph[itm],A,sol.T,flags,constant_parms.T).T
+        tecarray[itm+timeIdx,stIdx,polIdx]=sol[:,0]
+        clockarray[itm+timeIdx,stIdx,polIdx]=sol[:,1]
+        if useOffset:
+            offsetarray[itm+timeIdx,stIdx,polIdx]+=sol[:,2]
+        residual = ph[itm] - np.dot(A, sol.T)
+        residual = residual - residual[:, 0][:,np.newaxis]
+        residual = np.remainder(residual+np.pi, 2*np.pi) - np.pi       
+        residual[flags]=0
+        residualarray[np.ix_([itm+timeIdx],SBselect,stIdx,[polIdx])]=residual.reshape((1,nF,nSt,1))
+        chi2=np.sum(np.square(np.degrees(residual)))/(nSt*nF)
+        if chi2>chi2cut:
+            print("failure",chi2,sol)
+            succes=False
+            nrFail=0
+        else:
+            prevsol=np.copy(sol)
+#            print "succes",chi2,dTECArray.shape,dClockArray.shape
+            succes=True
+            initprevsol=True
+            nrFail+=1
+
+def add_to_h5_func(h5file,data,name='test'):
+    if name in h5file.root:
+        h5file.removeNode('/'+name)
+    myarray=h5file.createCArray(h5file.root,name,tab.Float32Atom(),shape=data.shape)
+    myarray[:]=data
+    myarray.flush()
+
+    
+
+def getAll(ionmodel,refstIdx=0,doClockTEC=True,doRM=False,add_to_h5=True,stationSelect='BA',label='fit',SBselect='all',allBaselines=True,useOffset=False,initFromPrevious=False,flagBadChannels=False,flagcut=1.5,chi2cut=30000.,removePhaseWraps=False,combine_pol=False,fixedClockforCS=False,timerange='all',CStec0=False,ignore_stations=["NOTHING_TO_IGNORE",]):
+    global tecarray
+    global clockarray
+    global offsetarray
+    global residualarray
+    if allBaselines and not doClockTEC:
+        doClockTEC=True
+
+    polshape=ionmodel.phases[:].shape[-1]
+    nT=ionmodel.times[:].shape[0]
+    if timerange=='all':
+        timerange=[0,nT]
+    nT=timerange[1]-timerange[0]
+    nF=ionmodel.freqs[:].shape[0]
+    freqs=ionmodel.freqs[:]
+    if SBselect=='all':
+        SBselect=np.ones(nF,dtype=bool)
+    if isinstance(SBselect,list) and len(SBselect)==2:
+        SBselect=np.logical_and(freqs>SBselect[0],freqs<SBselect[1])
+    if flagBadChannels:
+        rms=lambda x,y: np.sqrt(np.mean(np.square(x-np.mean(x,axis=y)),axis=y))
+        ph=ionmodel.phases[timerange[0]:timerange[1],:,1,0,0]-ionmodel.phases[timerange[0]:timerange[1],:,0,0,0]
+        #myrms1=rms(ph[:,SBselect],0)
+        myrms1=rms(ph[:],0)
+        freqselect=myrms1[SBselect]<flagcut*np.average(myrms1[SBselect])
+        cutlevel=flagcut*np.average(rms(ph[:,SBselect][:,freqselect],0))
+        SBselect=np.logical_and(SBselect,myrms1<cutlevel)
+        print("flagging",np.sum(np.logical_not(SBselect)),"channels")
+    freqs=freqs[SBselect]
+    if isinstance(stationSelect,str): 
+        stations=[st for st in list(ionmodel.stations[:]) if stationSelect]   
+    else:
+        stations=list(ionmodel.stations[:][stationSelect])
+    for ignore in ignore_stations:
+        stations=[st for st in stations if not ignore in st]
+    print("stations",stations)
+    if doClockTEC:
+        clockarray=np.zeros(ionmodel.times[:].shape+ionmodel.stations[:].shape+(2,))
+        tecarray=np.zeros(ionmodel.times[:].shape+ionmodel.stations[:].shape+(2,))
+        offsetarray=np.zeros(ionmodel.times[:].shape+ionmodel.stations[:].shape+(2,))
+        residualarray=np.zeros((len(ionmodel.times),len(ionmodel.freqs),len(ionmodel.stations),2))
+        ph=ionmodel.phases
+        amp=ionmodel.amplitudes
+    if doRM:
+        rmarray=np.zeros((len(ionmodel.times),len(ionmodel.stations)))
+        rotation_resarray=np.zeros((len(ionmodel.times),len(freqs),len(ionmodel.stations)))
+    if allBaselines:
+        #stationIndices=[list(ionmodel.stations[:]).index(st) for st in stations]
+        stationIndices=np.array([idxst in stations for idxst in ionmodel.stations[:]])
+        CSstations=np.array(['CS' in idxst for idxst in ionmodel.stations[:] if idxst in stations])
+        print('selected CS',CSstations)
+        for pol in range(2):
+            if combine_pol:
+                #phdata=ph[timerange[0]:timerange[1],:,:,0,(polshape-1)][:,SBselect][:,:,stationIndices]+ph[timerange[0]:timerange[1],:,:,0,0][:,SBselect][:,:,stationIndices]
+                ampdata=np.logical_or(amp[timerange[0]:timerange[1],:,:,0,(polshape-1)][:,SBselect][:,:,stationIndices]==1,amp[timerange[0]:timerange[1],:,:,0,0][:,SBselect][:,:,stationIndices]==1)
+
+                cdata1=1.*np.exp(1j*ph[timerange[0]:timerange[1],:,:,0,(polshape-1)][:,SBselect][:,:,stationIndices])
+                cdata2=1.*np.exp(1j*ph[timerange[0]:timerange[1],:,:,0,0][:,SBselect][:,:,stationIndices])
+                phdata=np.angle((cdata1+cdata2)/2.)
+                
+                #return phdata
+            else:
+                phdata=ph[timerange[0]:timerange[1],:,:,0,pol*(polshape-1)][:,SBselect][:,:,stationIndices]
+                ampdata=amp[timerange[0]:timerange[1],:,:,0,pol*(polshape-1)][:,SBselect][:,:,stationIndices]
+            if hasattr(ionmodel,'TEC') and initFromPrevious:
+                initSol=np.zeros((len(stations),2),dtype=np.float)
+                if len(ionmodel.TEC[:].shape)>3:
+                    initSol[:,0]=ionmodel.TEC[:][timerange[0],stationIndices,0,pol]
+                else:
+                    initSol[:,0]=ionmodel.TEC[:][timerange[0],stationIndices,pol]
+                initSol[:,1]=ionmodel.Clock[:][timerange[0],stationIndices,pol]
+                phdata+=ionmodel.clock_tec_offsets[:][timerange[0],stationIndices,pol]
+                offsetarray[:,stationIndices,pol]=ionmodel.clock_tec_offsets[:][timerange[0],stationIndices,pol]
+            else:
+                initSol=False
+
+            kwargs={'ph':phdata,
+                    'amp':ampdata,
+                    'freqs':freqs,
+                    'SBselect':SBselect,
+                    'polIdx':pol,
+                    'stIdx':stationIndices,
+                    'stations':stations,
+                    'useOffset':useOffset,
+                    'initSol':initSol,
+                    'chi2cut':chi2cut,
+                    'timeIdx':timerange[0]}
+            getClockTECBaselineFit(**kwargs)
+            if removePhaseWraps:
+                avgResiduals=np.average(residualarray[timerange[0]:timerange[1],:,:,pol],axis=0)
+                wraps,steps=getResidualPhaseWraps(avgResiduals,ionmodel.freqs[:])
+
+                #halfwraps=np.remainder(np.round(np.absolute(wraps[stationIndices]*2)),2)==1
+                #print "found halfwraps for",np.array(stations)[halfwraps]
+                if CStec0:
+                    pos=ionmodel.station_positions[:]
+                    lats=np.degrees(np.arctan2(pos[:,2],np.sqrt(pos[:,0]*pos[:,0]+pos[:,1]*pos[:,1])))
+                    lats=lats[stationIndices]
+                    lats-=lats[0]
+                    lons=np.degrees(np.arctan2(pos[:,1],pos[:,0]))
+                    lons=lons[stationIndices]
+                    lons-=lons[0]
+                    TEC=tecarray[timerange[0]:timerange[1],stationIndices,pol]-tecarray[timerange[0]:timerange[1],[0],pol]+steps[0]*(np.round(wraps[stationIndices])-np.round(wraps[0]))
+                    lonlat=np.concatenate((lons,lats)).reshape((2,)+lons.shape)
+
+                    slope=np.dot(np.diag(1./np.diag(np.dot(lonlat,lonlat.T))),np.dot(lonlat,TEC.T))
+                    chi2=np.sum(np.square(TEC-np.dot(lonlat.T,slope).T),axis=1)
+
+                    
+                    #slope=np.dot(1./(np.dot(lats.T,lats)), np.dot(lats.T,TEC.T))
+                    #chi2=np.sum(np.square(TEC-lats*slope[:,np.newaxis]),axis=1)
+                    chi2select=chi2<np.average(chi2)
+                    chi2select=chi2<np.average(chi2[chi2select])
+                    #return chi2,slope,TEC,lats
+                    print("wraps",wraps)
+                    print("slope",slope[:,chi2select][:,0])                    
+                    #offsets=-1*(np.average(TEC[chi2select]-lats*slope[chi2select][:,np.newaxis],axis=0))*2.*np.pi/steps[0]
+                    offsets=-1*(np.average(TEC[chi2select]-np.dot(slope.T,lonlat)[chi2select],axis=0))*2.*np.pi/steps[0]
+                    print("step",steps[0])
+                    print(offsets)
+                    remainingwraps=np.round(offsets/(2*np.pi))#-np.round(wraps[stationIndices])
+                    print(remainingwraps)
+                    wraps[stationIndices]+=remainingwraps
+
+                    #one more iteration
+                    if np.sum(np.absolute(remainingwraps))>0:
+                       TEC=tecarray[timerange[0]:timerange[1],stationIndices,pol]-tecarray[timerange[0]:timerange[1],[0],pol]+steps[0]*(np.round(wraps[stationIndices])-np.round(wraps[0]))
+                       slope=np.dot(np.diag(1./np.diag(np.dot(lonlat,lonlat.T))),np.dot(lonlat,TEC.T))
+                       chi2=np.sum(np.square(TEC-np.dot(lonlat.T,slope).T),axis=1)
+                       #slope=np.dot(1./(np.dot(lats.T,lats)), np.dot(lats.T,TEC.T))
+                       #chi2=np.sum(np.square(TEC-lats*slope[:,np.newaxis]),axis=1)
+                       chi2select=chi2<np.average(chi2)
+                       chi2select=chi2<np.average(chi2[chi2select])
+                       offsets=-1*(np.average(TEC[chi2select]-np.dot(slope.T,lonlat)[chi2select],axis=0))*2.*np.pi/steps[0]
+                       #offsets=-1*(np.average(TEC[chi2select]-lats*slope[chi2select][:,np.newaxis],axis=0))*2.*np.pi/steps[0]
+                       print("offsets itereation2:",offsets)
+                       remainingwraps=np.round(offsets/(2*np.pi))#-np.round(wraps[stationIndices])
+                       print("remaining wraps iteration 2",remainingwraps)
+                       wraps[stationIndices]+=remainingwraps
+                    #phdata[:,:,:]+=offsets
+                    phdata[:,:,CSstations]+=offsets[CSstations]
+                    offsetarray[:,stationIndices,pol]+=offsets
+                #clockarray[:,stationIndices,pol]+=(np.remainder(offsets+np.pi,2*np.pi)-np.pi)*steps[1]/(2*np.pi)
+                #!!!!!!!!!!!!!!!!TESTESTESTSETTE!!!
+                #phdata[:,:,np.arange(1,46,2)]+=0.01*np.arange(1,46,2)
+                initSol=np.zeros((len(stations),2),dtype=np.float)
+                #if combine_pol:
+                #    initSol[:,0]=tecarray[timerange[0],stationIndices,pol]+steps[0]*2*np.round(wraps[stationIndices])
+                #    initSol[:,1]=clockarray[timerange[0],stationIndices,pol]+steps[1]*2*np.round(wraps[stationIndices])
+                #else:
+                initSol[:,0]=tecarray[timerange[0],stationIndices,pol]+steps[0]*np.round(wraps[stationIndices])
+                initSol[:,1]=clockarray[timerange[0],stationIndices,pol]+steps[1]*np.round(wraps[stationIndices])
+                #initSol[:,1]=np.average(clockarray[:,stationIndices,pol]-clockarray[:,[0],pol],axis=0)+steps[1]*np.round(wraps[stationIndices])
+                print("final wraps",np.round(wraps[stationIndices]))
+                print("prev solutions", clockarray[timerange[0],stationIndices,pol])
+                print("init Clock with", initSol[:,1])
+                print("prev solutions TEC", tecarray[timerange[0],stationIndices,pol])
+                print("init TEC with", initSol[:,0])
+                if not(CStec0)  and np.all(np.round(wraps[stationIndices])==0):
+                    print("No need for phase unwrapping")
+                    continue;
+                kwargs={'ph':phdata,
+                        'amp':ampdata,
+                        'freqs':freqs,
+                        'SBselect':SBselect,
+                        'polIdx':pol,
+                        'stIdx':stationIndices,
+                        'stations':stations,
+                        'useOffset':useOffset,
+                        'initSol':initSol,
+                        'chi2cut':chi2cut,
+                        'timeIdx':timerange[0],
+                        'fixedClockforCS':fixedClockforCS}
+                getClockTECBaselineFit(**kwargs)
+            if combine_pol:
+                #tecarray/=2.
+                #clockarray/=2.
+                break;
+            
+    else:            
+      for ist,st in enumerate(stations):
+        print("getting values for station",st)
+        if doClockTEC:
+            if ist==refstIdx:
+                continue
+            for pol in range(2):
+                kwargs={'ph':ph[:,:,ist,0,pol*(polshape-1)][:,SBselect]-ph[:,:,refstIdx,0,pol*(polshape-1)][:,SBselect],
+                        'amp':amp[:,:,ist,0,pol*(polshape-1)][:,SBselect],
+                        'freqs':freqs,
+                        'SBselect':SBselect,
+                        'stationname':st,
+                        'stIdx':ist,
+                        'polIdx':pol}
+                getClockTEC(**kwargs)
+        if doRM:
+            if st==refstIdx:
+                continue;
+            rmarray[:,ist],rotation_resarray[:,:,ist]=getRM(ionmodel,st,refstIdx,SBselect=SBselect)
+
+
+    if add_to_h5:
+        if hasattr(ionmodel,'hdf5'):
+            h5file=ionmodel.hdf5
+        else:
+            h5file=ionmodel
+        if doClockTEC:
+            add_to_h5_func(h5file,clockarray,name='Clock')
+            add_to_h5_func(h5file,tecarray,name='TEC')
+            add_to_h5_func(h5file,offsetarray,name='clock_tec_offsets')
+            add_to_h5_func(h5file,residualarray,name='clock_tec_residuals')
+        if doRM:
+            add_to_h5_func(h5file,rmarray,name='rmtime')
+            add_to_h5_func(h5file,rotation_resarray,name='rm_residuals')
+    else:
+        if doClockTEC:
+            np.save('dclock_%s.npy'%(label), clockarray)
+            np.save('dTEC_%s.npy'%(label), tecarray)
+            np.save('offset_%s.npy'%(label), offsetarray)
+            np.save('residual_%s.npy'%(label), residualarray)
+        if doRM:
+            np.save('rm_%s.npy'%(label), rmarray)
+            np.save('rm_residuals_%s.npy'%(label), rotation_resarray)
+
+
+
+def SwapClockTECAxes(ionmodel):
+    print("swap axes will reshape your Clock and TEC solutions. The order of Clock is now times  x stations x polarizations and of TEC: times x stations x sources x polarizations")
+    TEC =ionmodel.TEC;
+    TECshape=TEC[:].shape
+    Clock =ionmodel.Clock;
+    Clockshape=Clock[:].shape
+    nT=ionmodel.times[:].shape[0]
+    nst=ionmodel.stations[:].shape[0]
+    nsources=ionmodel.N_sources
+    newshape=(nT,nsources,nst,2)
+    if TECshape==newshape:
+        print("nothing to be done for TEC")
+    else:
+        TEC=TEC[:]
+        indices=list(range(4)) #nT,st,nsources,pol
+        tmaxis=TECshape.index(nT)
+        indices[tmaxis]=0
+        staxis=TECshape.index(nst)
+        indices[staxis]=1
+        if len(TECshape)==3 or (nsources!=2):
+            polaxis=TECshape.index(2)
+            indices[polaxis]=3
+            if len(TECshape)>3:
+               nsaxis=TECshape.index(nsources) 
+            else:
+                TEC=TEC.reshape(TECshape+(1,))
+                nsaxis=3
+
+            indices[nsaxis]=2
+        else:
+            print("ambigous shape of TEC, try swapping by hand")
+        while tmaxis>0:
+            TEC=TEC.swapaxes(tmaxis,tmaxis-1)
+            indices[tmaxis]=indices[tmaxis-1]
+            tmaxis-=1
+            indices[tmaxis]=0
+        staxis=indices.index(1)
+        
+        while staxis>1:
+            TEC=TEC.swapaxes(staxis,staxis-1)
+            indices[staxis]=indices[staxis-1]
+            staxis-=1
+            indices[staxis]=1
+        srcaxis=indices.index(2)
+        
+        while srcaxis>2:
+            TEC=TEC.swapaxes(srcaxis,srcaxis-1)
+            indices[srcaxis]=indices[srcaxis-1]
+            srcaxis-=1
+            indices[srcaxis]=2
+        add_to_h5_func(ionmodel.hdf5,TEC,name='TEC')
+    newshape=(nT,nst,2)
+    if Clockshape==newshape:
+        print("nothing to be done for Clock")
+    else:
+        Clock=Clock[:]
+        indices=list(range(3)) #nT,st,pol
+        tmaxis=Clockshape.index(nT)
+        indices[tmaxis]=0
+        staxis=Clockshape.index(nst)
+        indices[staxis]=1
+        polaxis=Clockshape.index(2)
+        indices[polaxis]=2
+        while tmaxis>0:
+            Clock=Clock.swapaxes(tmaxis,tmaxis-1)
+            indices[tmaxis]=indices[tmaxis-1]
+            tmaxis-=1
+            indices[tmaxis]=0
+        staxis=indices.index(1)
+        
+        while staxis>1:
+            Clock=Clock.swapaxes(staxis,staxis-1)
+            indices[staxis]=indices[staxis-1]
+            staxis-=1
+            indices[staxis]=1
+       
+        add_to_h5_func(ionmodel.hdf5,Clock,name='Clock')
+      
+    
+def writeClocktoParmdb(ionmodel,average=False,create_new = True):
+    '''if average the average of both polarizations is used, snice BBS can handle only on value at the moment'''
+    if not hasattr(ionmodel,'Clock'):
+        print("No Clock solutions found, maybe you forgot to run the fit?")
+        return
+    Clock=ionmodel.Clock[:] # times x stations x pol
+    parms = {}
+    parm = {}
+    parm[ 'freqs' ] = np.array( [ .5e9 ] )
+    parm[ 'freqwidths' ] = np.array( [ 1.0e9 ] )
+    parm[ 'times' ] = ionmodel.times[:].ravel()
+    parm[ 'timewidths' ] = ionmodel.timewidths[:].ravel()
+    
+    stations=list(ionmodel.stations[:])
+    pol=ionmodel.polarizations[:]
+    for ist,st in enumerate(stations):
+        if average:
+            Clock_parm = parm.copy()
+            parmname = ':'.join(['Clock', st])
+            value=0.5*ionmodel.Clock[:, ist,0]+0.5*ionmodel.Clock[:, ist]
+            value*=1.e-9
+            Clock_parm[ 'values' ] = value
+            parms[ parmname ] = Clock_parm
+        else:
+            for  n_pol,ipol in enumerate(pol):
+                Clock_parm = parm.copy()
+                parmname = ':'.join(['Clock', str(ipol), st])
+                Clock_parm[ 'values' ] = 1.e-9*ionmodel.Clock[:, ist,n_pol]
+                parms[ parmname ] = Clock_parm
+    #return parms        
+    parmdbmain.store_parms( ionmodel.globaldb + '/ionosphere', parms, create_new = create_new)
+
+def writePhaseScreentoParmdb(ionmodel,create_new = True):
+    N_sources=ionmodel.N_sources
+    N_pol = min(len(ionmodel.polarizations),2)
+    if not hasattr(ionmodel,'globaldb'):
+        ionmodel.globaldb='./'
+    if not hasattr(ionmodel,'DirectionalGainEnable'):
+        ionmodel.DirectionalGainEnable=False
+    parms = {}
+    parm = {}
+    parm[ 'freqs' ] = np.array( [ .5e9 ] )
+    parm[ 'freqwidths' ] = np.array( [ 1.0e9 ] )
+    parm[ 'times' ] = ionmodel.times[:].ravel()
+    parm[ 'timewidths' ] = ionmodel.timewidths[:].ravel()
+
+    for n_pol in range(N_pol):
+
+        for n_station in range(len(ionmodel.stations[:])):
+            station = ionmodel.stations[n_station]
+            for n_source in range(N_sources):
+                if ionmodel.DirectionalGainEnable:
+                    source = ionmodel.sources[n_source]
+                    identifier = ':'.join([str(n_pol), station, source])
+                else:
+                    identifier = ':'.join([str(n_pol), station])
+
+            # TEC
+            TEC_parm = parm.copy()
+            parmname = ':'.join(['TEC', identifier])
+            TEC_parm[ 'values' ] = ionmodel.TEC[:,n_station,n_source,n_pol]
+            parms[ parmname ] = TEC_parm
+            
+            #TECfit
+            TECfit_parm = parm.copy()
+            parmname = ':'.join(['TECfit', identifier])
+            TECfit_parm[ 'values' ] = ionmodel.TECfit[:,n_station,n_source,n_pol]
+            parms[ parmname ] = TECfit_parm
+            
+            #TECfit_white
+            TECfit_white_parm = parm.copy()
+            parmname = ':'.join(['TECfit_white', identifier])
+            TECfit_white_parm[ 'values' ] = ionmodel.TECfit_white[:,n_station,n_source,n_pol]
+            parms[ parmname ] = TECfit_white_parm
+
+    # Piercepoints
+      
+    for n_station in range(len(ionmodel.stations)):
+     station = ionmodel.stations[n_station]
+     for n_source in range(N_sources):
+        if ionmodel.DirectionalGainEnable:
+           source = ionmodel.sources[n_source]
+           identifier = ':'.join([station, source])
+        else:
+           identifier = station
+        PiercepointX_parm = parm.copy()
+        parmname = ':'.join(['Piercepoint', 'X', identifier])
+        print(n_source, n_station)
+        x = ionmodel.piercepoints[:]['positions_xyz'][:,n_source, n_station,0]
+        PiercepointX_parm['values'] = x
+        parms[ parmname ] = PiercepointX_parm
+
+        PiercepointY_parm = parm.copy()
+        parmname = ':'.join(['Piercepoint', 'Y', identifier])
+        y = ionmodel.piercepoints[:]['positions_xyz'][:,n_source, n_station,1]
+        PiercepointY_parm['values'] = array(y)
+        parms[ parmname ] = PiercepointY_parm
+
+        PiercepointZ_parm = parm.copy()
+        parmname = ':'.join(['Piercepoint', 'Z', identifier])
+        z = ionmodel.piercepoints[:]['positions_xyz'][:,n_source, n_station,2]
+        PiercepointZ_parm['values'] = z
+        parms[ parmname ] = PiercepointZ_parm
+
+        Piercepoint_zenithangle_parm = parm.copy()
+        parmname = ':'.join(['Piercepoint', 'zenithangle', identifier])
+        za = ionmodel.piercepoints[:]['zenith_angles'][:,n_source, n_station]
+        Piercepoint_zenithangle_parm['values'] = za
+        parms[ parmname ] = Piercepoint_zenithangle_parm
+
+    time_start = ionmodel.times[0] - ionmodel.timewidths[0]/2
+    time_end = ionmodel.times[-1] + ionmodel.timewidths[-1]/2
+
+    parm[ 'times' ] = array([(time_start + time_end) / 2])
+    parm[ 'timewidths' ] = array([time_end - time_start])
+    
+    height_parm = parm.copy()
+    height_parm[ 'values' ] = array( ionmodel.piercepoints.attrs.height )
+    parms[ 'height' ] = height_parm
+    
+    beta_parm = parm.copy()
+    beta_parm[ 'values' ] = array( ionmodel.TECfit_white.attrs.beta )
+    parms[ 'beta' ] = beta_parm
+     
+    r_0_parm = parm.copy()
+    r_0_parm[ 'values' ] = array(  ionmodel.TECfit_white.attrs.r_0 )
+    parms[ 'r_0' ] = r_0_parm
+
+    parmdbmain.store_parms( ionmodel.globaldb + '/ionosphere', parms, create_new = create_new)
+
+
+def writePhaseScreenInfo(ionmodel,filename="clocktec.xmmlss.send"):
+    if not hasattr(ionmodel,'TEC'):
+        print('no fitted TEC information in you model, maybe you forgot to fit?')
+        return
+    
+
+    np.savez(filename,
+             clock=ionmodel.Clock[:],
+             tec=ionmodel.TEC[:],
+             offset=ionmodel.hdf5.root.clock_tec_offsets[:],
+             freqs=ionmodel.freqs[:],
+             radec=ionmodel.pointing[:],
+             antenna_names =ionmodel.stations[:],
+             antenna_positions=ionmodel.station_positions[:],
+             radeccal=ionmodel.source_positions[:])
+    
diff --git a/CEP/Calibration/ExpIon/src/format.py b/CEP/Calibration/ExpIon/src/format.py
new file mode 100644
index 0000000000000000000000000000000000000000..81b149105be23fda4c4117aceefd86e21e68f947
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/format.py
@@ -0,0 +1,486 @@
+"""
+Define a simple format for saving numpy arrays to disk with the full
+information about them.
+
+WARNING: Due to limitations in the interpretation of structured dtypes, dtypes
+with fields with empty names will have the names replaced by 'f0', 'f1', etc.
+Such arrays will not round-trip through the format entirely accurately. The
+data is intact; only the field names will differ. We are working on a fix for
+this.  This fix will not require a change in the file format. The arrays with
+such structures can still be saved and restored, and the correct dtype may be
+restored by using the `loadedarray.view(correct_dtype)` method.
+
+Format Version 1.0
+------------------
+
+The first 6 bytes are a magic string: exactly "\\\\x93NUMPY".
+
+The next 1 byte is an unsigned byte: the major version number of the file
+format, e.g. \\\\x01.
+
+The next 1 byte is an unsigned byte: the minor version number of the file
+format, e.g. \\\\x00. Note: the version of the file format is not tied to the
+version of the numpy package.
+
+The next 2 bytes form a little-endian unsigned short int: the length of the
+header data HEADER_LEN.
+
+The next HEADER_LEN bytes form the header data describing the array's format.
+It is an ASCII string which contains a Python literal expression of a
+dictionary. It is terminated by a newline ('\\\\n') and padded with spaces
+('\\\\x20') to make the total length of the magic string + 4 + HEADER_LEN be
+evenly divisible by 16 for alignment purposes.
+
+The dictionary contains three keys:
+
+    "descr" : dtype.descr
+        An object that can be passed as an argument to the numpy.dtype()
+        constructor to create the array's dtype.
+    "fortran_order" : bool
+        Whether the array data is Fortran-contiguous or not. Since
+        Fortran-contiguous arrays are a common form of non-C-contiguity, we
+        allow them to be written directly to disk for efficiency.
+    "shape" : tuple of int
+        The shape of the array.
+
+For repeatability and readability, the dictionary keys are sorted in alphabetic
+order. This is for convenience only. A writer SHOULD implement this if
+possible. A reader MUST NOT depend on this.
+
+Following the header comes the array data. If the dtype contains Python objects
+(i.e. dtype.hasobject is True), then the data is a Python pickle of the array.
+Otherwise the data is the contiguous (either C- or Fortran-, depending on
+fortran_order) bytes of the array. Consumers can figure out the number of bytes
+by multiplying the number of elements given by the shape (noting that shape=()
+means there is 1 element) by dtype.itemsize.
+
+"""
+
+import pickle
+
+import numpy
+from numpy.lib.utils import safe_eval
+
+
+MAGIC_PREFIX = '\x93NUMPY'
+MAGIC_LEN = len(MAGIC_PREFIX) + 2
+
+def magic(major, minor):
+    """ Return the magic string for the given file format version.
+
+    Parameters
+    ----------
+    major : int in [0, 255]
+    minor : int in [0, 255]
+
+    Returns
+    -------
+    magic : str
+
+    Raises
+    ------
+    ValueError if the version cannot be formatted.
+    """
+    if major < 0 or major > 255:
+        raise ValueError("major version must be 0 <= major < 256")
+    if minor < 0 or minor > 255:
+        raise ValueError("minor version must be 0 <= minor < 256")
+    return '%s%s%s' % (MAGIC_PREFIX, chr(major), chr(minor))
+
+def read_magic(fp):
+    """ Read the magic string to get the version of the file format.
+
+    Parameters
+    ----------
+    fp : filelike object
+
+    Returns
+    -------
+    major : int
+    minor : int
+    """
+    magic_str = fp.read(MAGIC_LEN)
+    if len(magic_str) != MAGIC_LEN:
+        msg = "could not read %d characters for the magic string; got %r"
+        raise ValueError(msg % (MAGIC_LEN, magic_str))
+    if magic_str[:-2] != MAGIC_PREFIX:
+        msg = "the magic string is not correct; expected %r, got %r"
+        raise ValueError(msg % (MAGIC_PREFIX, magic_str[:-2]))
+    major, minor = list(map(ord, magic_str[-2:]))
+    return major, minor
+
+def dtype_to_descr(dtype):
+    """
+    Get a serializable descriptor from the dtype.
+
+    The .descr attribute of a dtype object cannot be round-tripped through
+    the dtype() constructor. Simple types, like dtype('float32'), have
+    a descr which looks like a record array with one field with '' as
+    a name. The dtype() constructor interprets this as a request to give
+    a default name.  Instead, we construct descriptor that can be passed to
+    dtype().
+
+    Parameters
+    ----------
+    dtype : dtype
+        The dtype of the array that will be written to disk.
+
+    Returns
+    -------
+    descr : object
+        An object that can be passed to `numpy.dtype()` in order to
+        replicate the input dtype.
+
+    """
+    if dtype.names is not None:
+        # This is a record array. The .descr is fine.
+        # XXX: parts of the record array with an empty name, like padding bytes,
+        # still get fiddled with. This needs to be fixed in the C implementation
+        # of dtype().
+        return dtype.descr
+    else:
+        return dtype.str
+
+def header_data_from_array_1_0(array):
+    """ Get the dictionary of header metadata from a numpy.ndarray.
+
+    Parameters
+    ----------
+    array : numpy.ndarray
+
+    Returns
+    -------
+    d : dict
+        This has the appropriate entries for writing its string representation
+        to the header of the file.
+    """
+    d = {}
+    d['shape'] = array.shape
+    if array.flags.c_contiguous:
+        d['fortran_order'] = False
+    elif array.flags.f_contiguous:
+        d['fortran_order'] = True
+    else:
+        # Totally non-contiguous data. We will have to make it C-contiguous
+        # before writing. Note that we need to test for C_CONTIGUOUS first
+        # because a 1-D array is both C_CONTIGUOUS and F_CONTIGUOUS.
+        d['fortran_order'] = False
+
+    d['descr'] = dtype_to_descr(array.dtype)
+    return d
+
+def write_array_header_1_0(fp, d):
+    """ Write the header for an array using the 1.0 format.
+
+    Parameters
+    ----------
+    fp : filelike object
+    d : dict
+        This has the appropriate entries for writing its string representation
+        to the header of the file.
+    """
+    import struct
+    header = ["{"]
+    for key, value in sorted(d.items()):
+        # Need to use repr here, since we eval these when reading
+        header.append("'%s': %s, " % (key, repr(value)))
+    header.append("}")
+    header = "".join(header)
+    # Pad the header with spaces and a final newline such that the magic
+    # string, the header-length short and the header are aligned on a 16-byte
+    # boundary.  Hopefully, some system, possibly memory-mapping, can take
+    # advantage of our premature optimization.
+    current_header_len = MAGIC_LEN + 2 + len(header) + 1  # 1 for the newline
+    topad = 16 - (current_header_len % 16)
+    header = '%s%s\n' % (header, ' '*topad)
+    if len(header) >= (256*256):
+        raise ValueError("header does not fit inside %s bytes" % (256*256))
+    header_len_str = struct.pack('<H', len(header))
+    fp.write(header_len_str)
+    fp.write(header)
+
+def read_array_header_1_0(fp):
+    """
+    Read an array header from a filelike object using the 1.0 file format
+    version.
+
+    This will leave the file object located just after the header.
+
+    Parameters
+    ----------
+    fp : filelike object
+        A file object or something with a `.read()` method like a file.
+
+    Returns
+    -------
+    shape : tuple of int
+        The shape of the array.
+    fortran_order : bool
+        The array data will be written out directly if it is either C-contiguous
+        or Fortran-contiguous. Otherwise, it will be made contiguous before
+        writing it out.
+    dtype : dtype
+        The dtype of the file's data.
+
+    Raises
+    ------
+    ValueError :
+        If the data is invalid.
+
+    """
+    # Read an unsigned, little-endian short int which has the length of the
+    # header.
+    import struct
+    hlength_str = fp.read(2)
+    if len(hlength_str) != 2:
+        msg = "EOF at %s before reading array header length"
+        raise ValueError(msg % fp.tell())
+    header_length = struct.unpack('<H', hlength_str)[0]
+    header = fp.read(header_length)
+    if len(header) != header_length:
+        raise ValueError("EOF at %s before reading array header" % fp.tell())
+
+    # The header is a pretty-printed string representation of a literal Python
+    # dictionary with trailing newlines padded to a 16-byte boundary. The keys
+    # are strings.
+    #   "shape" : tuple of int
+    #   "fortran_order" : bool
+    #   "descr" : dtype.descr
+    try:
+        d = safe_eval(header)
+    except SyntaxError as e:
+        msg = "Cannot parse header: %r\nException: %r"
+        raise ValueError(msg % (header, e))
+    if not isinstance(d, dict):
+        msg = "Header is not a dictionary: %r"
+        raise ValueError(msg % d)
+    keys = list(d.keys())
+    keys.sort()
+    if keys != ['descr', 'fortran_order', 'shape']:
+        msg = "Header does not contain the correct keys: %r"
+        raise ValueError(msg % (keys,))
+
+    # Sanity-check the values.
+    if (not isinstance(d['shape'], tuple) or
+        not numpy.all([isinstance(x, int) for x in d['shape']])):
+        msg = "shape is not valid: %r"
+        raise ValueError(msg % (d['shape'],))
+    if not isinstance(d['fortran_order'], bool):
+        msg = "fortran_order is not a valid bool: %r"
+        raise ValueError(msg % (d['fortran_order'],))
+    try:
+        dtype = numpy.dtype(d['descr'])
+    except TypeError as e:
+        msg = "descr is not a valid dtype descriptor: %r"
+        raise ValueError(msg % (d['descr'],))
+
+    return d['shape'], d['fortran_order'], dtype
+
+def write_array(fp, array, version=(1,0)):
+    """
+    Write an array to an NPY file, including a header.
+
+    If the array is neither C-contiguous or Fortran-contiguous AND if the
+    filelike object is not a real file object, then this function will have
+    to copy data in memory.
+
+    Parameters
+    ----------
+    fp : filelike object
+        An open, writable file object or similar object with a `.write()`
+        method.
+    array : numpy.ndarray
+        The array to write to disk.
+    version : (int, int), optional
+        The version number of the format.
+
+    Raises
+    ------
+    ValueError
+        If the array cannot be persisted.
+    Various other errors
+        If the array contains Python objects as part of its dtype, the
+        process of pickling them may raise arbitrary errors if the objects
+        are not picklable.
+
+    """
+    if version != (1, 0):
+        msg = "we only support format version (1,0), not %s"
+        raise ValueError(msg % (version,))
+    fp.write(magic(*version))
+    write_array_header_1_0(fp, header_data_from_array_1_0(array))
+    if array.dtype.hasobject:
+        # We contain Python objects so we cannot write out the data directly.
+        # Instead, we will pickle it out with version 2 of the pickle protocol.
+        pickle.dump(array, fp, protocol=2)
+    elif array.flags.f_contiguous and not array.flags.c_contiguous:
+        # Use a suboptimal, possibly memory-intensive, but correct way to
+        # handle Fortran-contiguous arrays.
+        fp.write(array.data)
+    else:
+        if isinstance(fp, file):
+            array.tofile(fp)
+        else:
+            # XXX: We could probably chunk this using something like
+            # arrayterator.
+            fp.write(array.tostring('C'))
+
+def read_array(fp):
+    """
+    Read an array from an NPY file.
+
+    Parameters
+    ----------
+    fp : filelike object
+        If this is not a real file object, then this may take extra memory and
+        time.
+
+    Returns
+    -------
+    array : numpy.ndarray
+        The array from the data on disk.
+
+    Raises
+    ------
+    ValueError
+        If the data is invalid.
+
+    """
+    version = read_magic(fp)
+    if version != (1, 0):
+        msg = "only support version (1,0) of file format, not %r"
+        raise ValueError(msg % (version,))
+    shape, fortran_order, dtype = read_array_header_1_0(fp)
+    if len(shape) == 0:
+        count = 1
+    else:
+        count = numpy.multiply.reduce(shape)
+
+    # Now read the actual data.
+    if dtype.hasobject:
+        # The array contained Python objects. We need to unpickle the data.
+        array = pickle.load(fp)
+    else:
+        if isinstance(fp, file):
+            # We can use the fast fromfile() function.
+            array = numpy.fromfile(fp, dtype=dtype, count=count)
+        else:
+            # This is not a real file. We have to read it the memory-intensive
+            # way.
+            # XXX: we can probably chunk this to avoid the memory hit.
+            data = fp.read(count * dtype.itemsize)
+            array = numpy.fromstring(data, dtype=dtype, count=count)
+
+        if fortran_order:
+            array.shape = shape[::-1]
+            array = array.transpose()
+        else:
+            array.shape = shape
+
+    return array
+
+
+def open_memmap(filename, mode='r+', dtype=None, shape=None,
+                fortran_order=False, version=(1,0)):
+    """
+    Open a .npy file as a memory-mapped array.
+
+    This may be used to read an existing file or create a new one.
+
+    Parameters
+    ----------
+    filename : str
+        The name of the file on disk. This may not be a file-like object.
+    mode : str, optional
+        The mode to open the file with. In addition to the standard file modes,
+        'c' is also accepted to mean "copy on write". See `numpy.memmap` for
+        the available mode strings.
+    dtype : dtype, optional
+        The data type of the array if we are creating a new file in "write"
+        mode.
+    shape : tuple of int, optional
+        The shape of the array if we are creating a new file in "write"
+        mode.
+    fortran_order : bool, optional
+        Whether the array should be Fortran-contiguous (True) or
+        C-contiguous (False) if we are creating a new file in "write" mode.
+    version : tuple of int (major, minor)
+        If the mode is a "write" mode, then this is the version of the file
+        format used to create the file.
+
+    Returns
+    -------
+    marray : numpy.memmap
+        The memory-mapped array.
+
+    Raises
+    ------
+    ValueError
+        If the data or the mode is invalid.
+    IOError
+        If the file is not found or cannot be opened correctly.
+
+    See Also
+    --------
+    numpy.memmap
+
+    """
+    if not isinstance(filename, str):
+        raise ValueError("Filename must be a string.  Memmap cannot use" \
+                         " existing file handles.")
+
+    if 'w' in mode:
+        # We are creating the file, not reading it.
+        # Check if we ought to create the file.
+        if version != (1, 0):
+            msg = "only support version (1,0) of file format, not %r"
+            raise ValueError(msg % (version,))
+        # Ensure that the given dtype is an authentic dtype object rather than
+        # just something that can be interpreted as a dtype object.
+        dtype = numpy.dtype(dtype)
+        if dtype.hasobject:
+            msg = "Array can't be memory-mapped: Python objects in dtype."
+            raise ValueError(msg)
+        d = dict(
+            descr=dtype_to_descr(dtype),
+            fortran_order=fortran_order,
+            shape=shape,
+        )
+        # If we got here, then it should be safe to create the file.
+        fp = open(filename, mode+'b')
+        try:
+            fp.write(magic(*version))
+            write_array_header_1_0(fp, d)
+            offset = fp.tell()
+        finally:
+            fp.close()
+    else:
+        # Read the header of the file first.
+        fp = open(filename, 'rb')
+        try:
+            version = read_magic(fp)
+            if version != (1, 0):
+                msg = "only support version (1,0) of file format, not %r"
+                raise ValueError(msg % (version,))
+            shape, fortran_order, dtype = read_array_header_1_0(fp)
+            if dtype.hasobject:
+                msg = "Array can't be memory-mapped: Python objects in dtype."
+                raise ValueError(msg)
+            offset = fp.tell()
+        finally:
+            fp.close()
+
+    if fortran_order:
+        order = 'F'
+    else:
+        order = 'C'
+
+    # We need to change a write-only mode to a read-write mode since we've
+    # already written data to the file.
+    if mode == 'w+':
+        mode = 'r+'
+
+    marray = numpy.memmap(filename, dtype=dtype, shape=shape, order=order,
+        mode=mode, offset=offset)
+
+    return marray
diff --git a/CEP/Calibration/ExpIon/src/io.py b/CEP/Calibration/ExpIon/src/io.py
new file mode 100644
index 0000000000000000000000000000000000000000..06b09f7eab83fa494dfc9b8141c2381f68e46630
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/io.py
@@ -0,0 +1,308 @@
+import numpy as np
+from . import format
+import io
+import os
+import itertools
+import sys
+
+from pickle import load as _cload, loads
+
+_file = file
+
+def seek_gzip_factory(f):
+    """Use this factory to produce the class so that we can do a lazy
+    import on gzip.
+
+    """
+    import gzip, new
+
+    def seek(self, offset, whence=0):
+        # figure out new position (we can only seek forwards)
+        if whence == 1:
+            offset = self.offset + offset
+
+        if whence not in [0, 1]:
+            raise IOError("Illegal argument")
+
+        if offset < self.offset:
+            # for negative seek, rewind and do positive seek
+            self.rewind()
+            count = offset - self.offset
+            for i in range(count // 1024):
+                self.read(1024)
+            self.read(count % 1024)
+
+    def tell(self):
+        return self.offset
+
+    if isinstance(f, str):
+        f = gzip.GzipFile(f)
+
+    f.seek = new.instancemethod(seek, f)
+    f.tell = new.instancemethod(tell, f)
+
+    return f
+
+def zipfile_factory(*args, **kwargs):
+    """Allow the Zip64 extension, which is supported in Python >= 2.5.
+
+    """
+    from zipfile import ZipFile
+    if sys.version_info > (2, 4):
+        kwargs['allowZip64'] = True
+    return ZipFile(*args, **kwargs)
+
+class BagObj(object):
+    """A simple class that converts attribute lookups to
+    getitems on the class passed in.
+    """
+    def __init__(self, obj):
+        self._obj = obj
+    def __getattribute__(self, key):
+        try:
+            return object.__getattribute__(self, '_obj')[key]
+        except KeyError:
+            raise AttributeError(key)
+
+class NpzFile(object):
+    """A dictionary-like object with lazy-loading of files in the zipped
+    archive provided on construction.
+
+    The arrays and file strings are lazily loaded on either
+    getitem access using obj['key'] or attribute lookup using obj.f.key
+
+    A list of all files (without .npy) extensions can be obtained
+    with .files and the ZipFile object itself using .zip
+    """
+    def __init__(self, fid):
+        # Import is postponed to here since zipfile depends on gzip, an optional
+        # component of the so-called standard library.
+        import zipfile
+        _zip = zipfile_factory(fid)
+        self._files = _zip.namelist()
+        self.files = []
+        for x in self._files:
+            if x.endswith('.npy'):
+                self.files.append(x[:-4])
+            else:
+                self.files.append(x)
+        self.zip = _zip
+        self.f = BagObj(self)
+
+    def __getitem__(self, key):
+        # FIXME: This seems like it will copy strings around
+        #   more than is strictly necessary.  The zipfile
+        #   will read the string and then
+        #   the format.read_array will copy the string
+        #   to another place in memory.
+        #   It would be better if the zipfile could read
+        #   (or at least uncompress) the data
+        #   directly into the array memory.
+        member = 0
+        if key in self._files:
+            member = 1
+        elif key in self.files:
+            member = 1
+            key += '.npy'
+        if member:
+            bytes = self.zip.read(key)
+            if bytes.startswith(format.MAGIC_PREFIX):
+                value = io.StringIO(bytes)
+                return format.read_array(value)
+            else:
+                return bytes
+        else:
+            raise KeyError("%s is not a file in the archive" % key)
+
+def load(file, mmap_mode=None):
+    """
+    Load a pickled, ``.npy``, or ``.npz`` binary file.
+
+    Parameters
+    ----------
+    file : file-like object or string
+        The file to read.  It must support ``seek()`` and ``read()`` methods.
+    mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional
+        If not None, then memory-map the file, using the given mode
+        (see `numpy.memmap`).  The mode has no effect for pickled or
+        zipped files.
+        A memory-mapped array is stored on disk, and not directly loaded
+        into memory.  However, it can be accessed and sliced like any
+        ndarray.  Memory mapping is especially useful for accessing
+        small fragments of large files without reading the entire file
+        into memory.
+
+    Returns
+    -------
+    result : array, tuple, dict, etc.
+        Data stored in the file.
+
+    Raises
+    ------
+    IOError
+        If the input file does not exist or cannot be read.
+
+    Notes
+    -----
+    - If the file contains pickle data, then whatever is stored in the
+      pickle is returned.
+    - If the file is a ``.npy`` file, then an array is returned.
+    - If the file is a ``.npz`` file, then a dictionary-like object is
+      returned, containing ``{filename: array}`` key-value pairs, one for
+      each file in the archive.
+
+    Examples
+    --------
+    Store data to disk, and load it again:
+
+    >>> np.save('/tmp/123', np.array([[1, 2, 3], [4, 5, 6]]))
+    >>> np.load('/tmp/123.npy')
+    array([[1, 2, 3],
+           [4, 5, 6]])
+
+    Mem-map the stored array, and then access the second row
+    directly from disk:
+
+    >>> X = np.load('/tmp/123.npy', mmap_mode='r')
+    >>> X[1, :]
+    memmap([4, 5, 6])
+
+    """
+    import gzip
+
+    if isinstance(file, str):
+        fid = _file(file,"rb")
+    elif isinstance(file, gzip.GzipFile):
+        fid = seek_gzip_factory(file)
+    else:
+        fid = file
+
+    # Code to distinguish from NumPy binary files and pickles.
+    _ZIP_PREFIX = 'PK\x03\x04'
+    N = len(format.MAGIC_PREFIX)
+    magic = fid.read(N)
+    fid.seek(-N,1) # back-up
+    if magic.startswith(_ZIP_PREFIX):  # zip-file (assume .npz)
+        return NpzFile(fid)
+    elif magic == format.MAGIC_PREFIX: # .npy file
+        if mmap_mode:
+            return format.open_memmap(file, mode=mmap_mode)
+        else:
+            return format.read_array(fid)
+    else:  # Try a pickle
+        try:
+            return _cload(fid)
+        except:
+            raise IOError("Failed to interpret file %s as a pickle" % repr(file))
+
+def save(file, arr):
+    """
+    Save an array to a binary file in NumPy format.
+
+    Parameters
+    ----------
+    f : file or string
+        File or filename to which the data is saved.  If the filename
+        does not already have a ``.npy`` extension, it is added.
+    x : array_like
+        Array data.
+
+    See Also
+    --------
+    savez : Save several arrays into an .npz compressed archive
+    savetxt : Save an array to a file as plain text
+
+    Examples
+    --------
+    >>> from tempfile import TemporaryFile
+    >>> outfile = TemporaryFile()
+
+    >>> x = np.arange(10)
+    >>> np.save(outfile, x)
+
+    >>> outfile.seek(0)
+    >>> np.load(outfile)
+    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+
+    """
+    if isinstance(file, str):
+        if not file.endswith('.npy'):
+            file = file + '.npy'
+        fid = open(file, "wb")
+    else:
+        fid = file
+
+    arr = np.asanyarray(arr)
+    format.write_array(fid, arr)
+
+def savez(file, *args, **kwds):
+    """
+    Save several arrays into a single, compressed file with extension ".npz"
+
+    If keyword arguments are given, the names for variables assigned to the
+    keywords are the keyword names (not the variable names in the caller).
+    If arguments are passed in with no keywords, the corresponding variable
+    names are arr_0, arr_1, etc.
+
+    Parameters
+    ----------
+    file : Either the filename (string) or an open file (file-like object)
+        If file is a string, it names the output file.  ".npz" will be appended
+        if it is not already there.
+    args : Arguments
+        Any function arguments other than the file name are variables to save.
+        Since it is not possible for Python to know their names outside the
+        savez function, they will be saved with names "arr_0", "arr_1", and so
+        on.  These arguments can be any expression.
+    kwds : Keyword arguments
+        All keyword=value pairs cause the value to be saved with the name of
+        the keyword.
+
+    See Also
+    --------
+    save : Save a single array to a binary file in NumPy format
+    savetxt : Save an array to a file as plain text
+
+    Notes
+    -----
+    The .npz file format is a zipped archive of files named after the variables
+    they contain.  Each file contains one variable in .npy format.
+
+    """
+
+    # Import is postponed to here since zipfile depends on gzip, an optional
+    # component of the so-called standard library.
+    import zipfile
+
+    if isinstance(file, str):
+        if not file.endswith('.npz'):
+            file = file + '.npz'
+
+    namedict = kwds
+    for i, val in enumerate(args):
+        key = 'arr_%d' % i
+        if key in list(namedict.keys()):
+            raise ValueError("Cannot use un-named variables and keyword %s" % key)
+        namedict[key] = val
+
+    zip = zipfile_factory(file, mode="w")
+
+    # Place to write temporary .npy files
+    #  before storing them in the zip
+    import tempfile
+    direc = tempfile.gettempdir()
+    todel = []
+
+    for key, val in namedict.items():
+        fname = key + '.npy'
+        filename = os.path.join(direc, fname)
+        todel.append(filename)
+        fid = open(filename,'wb')
+        format.write_array(fid, np.asanyarray(val))
+        fid.close()
+        zip.write(filename, arcname=fname)
+
+    zip.close()
+    for name in todel:
+        os.remove(name)
+
diff --git a/CEP/Calibration/ExpIon/src/ionosphere.py b/CEP/Calibration/ExpIon/src/ionosphere.py
new file mode 100755
index 0000000000000000000000000000000000000000..429a0e597fcf44f01bba1070aeabd78d2f21295c
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/ionosphere.py
@@ -0,0 +1,1380 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2007
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id$
+###############################################################################
+
+# import Python modules
+import sys
+import os
+from datetime import *
+from math import *
+import time
+import re
+
+# import 3rd party modules
+  
+import numpy
+from pylab import *
+import scipy.optimize
+
+# import user modules
+#from files import *
+from . import client
+from .acalc import *
+from . import sphere
+import lofar.parmdb
+import lofar.parameterset
+import pyrap.tables as pt
+from .mpfit import *
+from .error import *
+from . import readms
+from . import io
+
+from . import parmdbmain
+import tables
+
+
+###############################################################################
+
+class IonosphericModel:
+   """IonosphericModel class is the main interface to the functions impelmented in the ionosphere module"""
+
+   def __init__( self, gdsfiles, clusterdesc = '', sky_name = 'sky', instrument_name = 'instrument', 
+         sources = [], stations = [], polarizations = [], GainEnable = False, DirectionalGainEnable = False,
+         PhasorsEnable = False, RotationEnable = False, estimate_gradient = True,
+         remove_gradient = True, equal_weights = False, normalize_weights = True, save_pierce_points = True,
+         globaldb = 'globaldb', movie_file_name = '', format = 'mpg', plot_gradient = True, xy_range = [ - 0.5, 0.5 ],
+         e_steps = 4, print_info = False, estimate_offsets = False,
+         include_airmass = True, solution_version = 0 ):
+
+      """
+      gdsfiles can be either a list of gdsfiles or a single string that will be interpreted as the name
+         of a file that was created by the save method
+      clusterdesc is the name of the cluster description file
+      sky_name 
+      instrument_name
+      sources
+      stations
+      
+      """
+
+      # Check whether to open a list of parmdbs or a previously stored IonosphericModel object
+      self.DirectionalGainEnable = DirectionalGainEnable
+      
+      if len(gdsfiles) == 1 and os.path.isdir( gdsfiles[0] ):
+         self.load_globaldb( gdsfiles[0] )
+      else:
+         self.GainEnable = GainEnable
+         self.DirectionalGainEnable = DirectionalGainEnable
+         self.PhasorsEnable = PhasorsEnable
+         self.RotationEnable = RotationEnable
+         self.polarizations = polarizations
+         self.N_pol = len(polarizations)
+         print("RotationEnable:", self.RotationEnable)
+         self.load_gds(gdsfiles, clusterdesc, globaldb, sky_name, instrument_name, stations, sources)
+
+   def load_globaldb ( self, globaldb ) :
+      self.globaldb = globaldb
+      self.hdf5 = tables.openFile(os.path.join(globaldb , 'ionmodel.hdf5'), 'r+')
+
+      self.stations = self.hdf5.root.stations.cols.name
+      self.station_positions = self.hdf5.root.stations.cols.position
+      self.array_center = self.hdf5.root.array_center
+      self.N_stations = len(self.stations)
+      
+      
+      #ionospheredbname = os.path.join(globaldb, 'ionosphere')
+      #ionospheredb = lofar.parmdb.parmdb( ionospheredbname )
+      
+      #self.stations = get_station_list_from_ionospheredb( ionospheredb )
+
+      #antenna_table = pt.table( globaldb + "/ANTENNA")
+      #name_col = antenna_table.getcol('NAME')
+      #position_col = antenna_table.getcol( 'POSITION' )
+      #self.station_positions = [position_col[name_col.index(station_name)] for station_name in self.stations]
+      #antenna_table.close()
+
+      #field_table = pt.table( globaldb + "/FIELD")
+      #phase_dir_col = field_table.getcol('PHASE_DIR')
+      #self.pointing = phase_dir_col[0,0,:]
+      #field_table.close()
+
+      #self.sources = get_source_list_from_ionospheredb( ionospheredb )      # source positions
+
+      self.sources = self.hdf5.root.sources[:]['name']
+      self.source_positions = self.hdf5.root.sources[:]['position']
+      self.N_sources = len(self.sources)
+      self.N_piercepoints = self.N_sources * self.N_stations
+      
+      self.pointing = self.hdf5.root.pointing
+
+      self.freqs = self.hdf5.root.freqs
+      self.polarizations = self.hdf5.root.polarizations
+      self.N_pol = len(self.polarizations)
+
+      self.phases = self.hdf5.root.phases
+      self.flags = self.hdf5.root.flags
+
+      for varname in ['amplitudes', 'rotation', 'Clock', 'TEC', 'TECfit', 'TECfit_white', 'offsets', \
+                      'times', 'timewidths', 'piercepoints', 'facets', 'facet_piercepoints', 'n_list', \
+                      'STEC_facets'] :
+         if varname in self.hdf5.root:
+            self.__dict__.update( [(varname, self.hdf5.getNode(self.hdf5.root, varname))] )
+            
+      #if 'Clock' in self.hdf5.root : self.Clock = self.hdf5.root.Clock
+      #if 'TEC' in self.hdf5.root: self.TEC = self.hdf5.root.TEC
+      #if 'TECfit' in self.hdf5.root: self.TEC = self.hdf5.root.TECfit
+      #if 'TECfit_white' in self.hdf5.root: self.TEC = self.hdf5.root.TECfit_white
+      #if 'offsets' in self.hdf5.root: self.TEC = self.hdf5.root.offsets
+      #if 'piercepoints' in self.hdf5.root: self.TEC = self.hdf5.root.piercepoints
+         
+      self.N_stations = len(self.stations)
+      self.N_sources = len(self.sources)
+
+   def load_gds( self, gdsfiles, clusterdesc, globaldb, sky_name, instrument_name, stations, sources ):
+
+      self.gdsfiles = gdsfiles
+      self.instrument_name = instrument_name
+      self.globaldb = globaldb
+
+      try:
+         os.mkdir(globaldb)
+      except OSError:
+         pass
+
+      self.instrumentdb_name_list = []
+      for gdsfile in gdsfiles:
+         instrumentdb_name = os.path.splitext(gdsfile)[0] + os.path.extsep + instrument_name
+         if not os.path.exists(instrumentdb_name):
+            instrumentdb_name = make_instrumentdb( gdsfile, instrument_name, globaldb )
+         self.instrumentdb_name_list.append(instrumentdb_name)
+         
+      gdsfiles = []   
+      for (idx, gdsfile) in zip(list(range(len(self.gdsfiles))), self.gdsfiles):
+         gdsfiles.extend( splitgds( gdsfile, wd = self.globaldb, id = 'part-%i' % idx) )
+      self.gdsfiles = gdsfiles
+
+      instrumentdb_name_list = []   
+      for (idx, instrumentdb_name) in zip(list(range(len(self.instrumentdb_name_list))), self.instrumentdb_name_list):
+         instrumentdb_name_list.extend( splitgds( instrumentdb_name, wd = self.globaldb, id = 'instrument-%i' % idx) )
+      self.instrumentdb_name_list = instrumentdb_name_list
+      
+      instrumentdb_name_list = []
+      for instrumentdb_name in self.instrumentdb_name_list :
+         instrumentdb_parset = lofar.parameterset.parameterset( instrumentdb_name )
+         instrumentdbfilename = instrumentdb_parset.getString( "Part0.FileName" )
+         instrumentdbhostname = instrumentdb_parset.getString( "Part0.FileSys" ).split(':')[0]
+         instrumentdb_name = os.path.splitext( instrumentdb_name )[0]
+         if not os.path.exists(instrumentdb_name) : 
+            os.system( "scp -r %s:%s %s" % ( instrumentdbhostname, instrumentdbfilename, instrumentdb_name ) )
+         instrumentdb_name_list.append(instrumentdb_name)
+      self.instrumentdb_name_list = instrumentdb_name_list
+      
+      gdsfile = gdsfiles[0]
+      instrumentdb = lofar.parmdb.parmdb( self.instrumentdb_name_list[0] )
+      
+      self.hdf5 = tables.openFile(os.path.join(globaldb , 'ionmodel.hdf5'), 'w')
+
+      gdsparset = lofar.parameterset.parameterset( gdsfile )
+      
+      skydbfilename = os.path.join(gdsparset.getString( "Part0.FileName" ), sky_name)
+      skydbhostname = gdsparset.getString( "Part0.FileSys" ).split(':')[0]
+      skydbname = globaldb + "/sky"
+      if not os.path.exists(skydbname) : 
+         os.system( "scp -r %s:%s %s" % ( skydbhostname, skydbfilename, skydbname ) )
+      skydb = lofar.parmdb.parmdb( skydbname )
+
+      gdsparset = lofar.parameterset.parameterset( gdsfile )
+      msname = gdsparset.getString( "Part0.FileName" )
+      mshostname = gdsparset.getString( "Part0.FileSys" ).split(':')[0]
+      antenna_table_name = os.path.join( globaldb, "ANTENNA")
+      if not os.path.exists(antenna_table_name) :
+         os.system( "scp -r %s:%s/ANTENNA %s" % ( mshostname, msname, antenna_table_name ) )
+      field_table_name = os.path.join( globaldb, "FIELD" )
+      if not os.path.exists( field_table_name ) :
+         os.system( "scp -r %s:%s/FIELD %s" % ( mshostname, msname, field_table_name ) )
+
+      if len( stations ) == 0 :
+         stations = ["*"]
+      self.stations = get_station_list( instrumentdb, stations, self.DirectionalGainEnable )
+      self.N_stations = len(self.stations)
+      
+      antenna_table = pt.table( globaldb + "/ANTENNA")
+      name_col = antenna_table.getcol('NAME')
+      position_col = antenna_table.getcol( 'POSITION' )
+      self.station_positions = [position_col[name_col.index(station_name)] for station_name in self.stations]
+      antenna_table.close()
+      
+      station_table = self.hdf5.createTable(self.hdf5.root, 'stations', {'name': tables.StringCol(40), 'position':tables.Float64Col(3)})
+      row = station_table.row
+      for (station, position) in zip(self.stations, self.station_positions) : 
+         row['name'] = station
+         row['position'] = position
+         row.append()
+      station_table.flush()
+      
+      self.array_center = array( self.station_positions ).mean(axis=0).tolist()
+      self.hdf5.createArray(self.hdf5.root, 'array_center', self.array_center)
+
+      field_table = pt.table( globaldb + "/FIELD")
+      phase_dir_col = field_table.getcol('PHASE_DIR')
+      self.pointing = phase_dir_col[0,0,:]
+      field_table.close()
+      self.hdf5.createArray(self.hdf5.root, 'pointing', self.pointing)
+
+      if self.DirectionalGainEnable or self.RotationEnable:
+         if len( sources ) == 0:
+            sources = ["*"]
+         self.sources = get_source_list( instrumentdb, sources )
+         self.source_positions = []
+         for source in self.sources :
+            try:
+               RA = skydb.getDefValues( 'Ra:' + source )['Ra:' + source][0][0]
+               dec = skydb.getDefValues( 'Dec:' + source )['Dec:' + source][0][0]
+            except KeyError:
+               # Source not found in skymodel parmdb, try to find components
+               RA = numpy.array(list(skydb.getDefValues( 'Ra:' + source + '.*' ).values())).mean()
+               dec = numpy.array(list(skydb.getDefValues( 'Dec:' + source + '.*' ).values())).mean()
+            self.source_positions.append([RA, dec])
+      else:
+         self.sources = ["Pointing"]
+         self.source_positions = [list(self.pointing)]
+      self.N_sources = len(self.sources)
+
+      source_table = self.hdf5.createTable(self.hdf5.root, 'sources', {'name': tables.StringCol(40), 'position':tables.Float64Col(2)})
+      row = source_table.row
+      for (source, position) in zip(self.sources, self.source_positions) :
+         row['name'] = source
+         row['position'] = position
+         row.append()
+      source_table.flush()
+
+      if self.PhasorsEnable:
+         infix = ('Ampl', 'Phase')
+      else:
+         infix = ('Real', 'Imag')
+
+      if self.GainEnable :
+         parmname0 = ':'.join(['Gain', str(self.polarizations[0]), str(self.polarizations[0]), infix[1], self.stations[0]])
+         v0 = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]
+      if self.DirectionalGainEnable :
+         parmname0 = ':'.join(['DirectionalGain', str(self.polarizations[0]), str(self.polarizations[0]), infix[1], self.stations[0], self.sources[0]])
+         v0 = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]
+
+      self.freqs = []
+      self.freqwidths = []
+      
+      # First collect all frequencies 
+      # We need them beforehand to sort the frequencies (frequencies are not necessarily given in sorted order)
+      for instrumentdb_name in self.instrumentdb_name_list:
+        try:
+          instrumentdb = lofar.parmdb.parmdb( instrumentdb_name )
+          v0 = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]
+          freqs = v0['freqs']
+          self.freqs = numpy.concatenate([self.freqs, freqs])
+          self.freqwidths = numpy.concatenate([self.freqwidths, v0['freqwidths']])
+        except:
+          print("Error opening " + instrumentdb_name)
+          exit()
+      # Sort frequencies, find both the forward and inverse mapping
+      # Mappings are such that
+      #    sorted_freqs = unsorted_freqs[sorted_freq_idx]
+      #    sorted_freqs[inverse_sorted_freq_idx] = unsorted_freqs
+      # We will use the following form
+      #    sorted_freqs[inverse_sorted_freq_idx[selection]] = unsorted_freqs[selection]
+      # to process chunks (=selections) of unsorted data and store them in sorted order
+      sorted_freq_idx = sorted(list(range(len(self.freqs))), key = lambda idx: self.freqs[idx])
+      inverse_sorted_freq_idx = sorted(list(range(len(self.freqs))), key = lambda idx: sorted_freq_idx[idx])
+      
+      self.freqs = self.freqs[sorted_freq_idx]
+      self.freqwidths = self.freqwidths[sorted_freq_idx]
+      self.hdf5.createArray(self.hdf5.root, 'freqs', self.freqs)
+      self.N_freqs = len(self.freqs)
+      
+      self.times = v0['times']
+      self.timewidths = v0['timewidths']
+      self.hdf5.createArray(self.hdf5.root, 'times', self.times)
+      self.hdf5.createArray(self.hdf5.root, 'timewidths', self.timewidths)
+      self.N_times = len( self.times )
+
+      self.hdf5.createArray(self.hdf5.root, 'polarizations', self.polarizations)
+      
+      chunkshape = (1024 , 32, 1, 1, 1)
+      self.phases = self.hdf5.createCArray(self.hdf5.root, 'phases', tables.Float32Atom(), shape=(self.N_times, self.N_freqs, self.N_stations, self.N_sources, self.N_pol), chunkshape = chunkshape)
+      self.amplitudes = self.hdf5.createCArray(self.hdf5.root, 'amplitudes', tables.Float32Atom(), shape=(self.N_times, self.N_freqs, self.N_stations, self.N_sources, self.N_pol), chunkshape = chunkshape)
+      if self.RotationEnable:
+        self.rotation = self.hdf5.createCArray(self.hdf5.root, 'rotation', tables.Float32Atom(), shape=(self.N_times, self.N_freqs, self.N_stations, self.N_sources), chunkshape = chunkshape[:-1])
+      fillarray(self.amplitudes, 1.0)
+      self.flags = self.hdf5.createCArray(self.hdf5.root, 'flags', tables.Float32Atom(), shape=(self.N_times, self.N_freqs))
+
+      freq_idx = 0
+      for gdsfile, instrumentdb_name, gdsfile_idx in zip(gdsfiles, self.instrumentdb_name_list, list(range(len(gdsfiles)))) :
+         print(('-Reading %s (%i/%i)' % (gdsfile, gdsfile_idx+1, len(gdsfiles))), end=' ')
+         shapemismatch = False
+
+         instrumentdb = lofar.parmdb.parmdb( instrumentdb_name )
+         v0 = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]
+         freqs = v0['freqs']
+         N_freqs = len(freqs)
+         sorted_freq_selection = inverse_sorted_freq_idx[freq_idx:freq_idx+N_freqs]
+            
+         try:
+            self.flags[:, sorted_freq_selection] = instrumentdb.getValuesGrid('flags')['flags']['values']
+         except KeyError:
+            pass
+         
+         for pol, pol_idx in zip(self.polarizations, list(range(len(self.polarizations)))):
+            for station, station_idx in zip(self.stations, list(range(len(self.stations)))):
+              if self.GainEnable:
+                 parmname0 = ':'.join(['Gain', str(pol), str(pol), infix[0], station])
+                 parmname1 = ':'.join(['Gain', str(pol), str(pol), infix[1], station])
+                 if self.PhasorsEnable:
+                   gain_phase = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                   self.phases[:, sorted_freq_selection, station_idx, :, pol_idx] = resize(gain_phase.T, (self.N_sources, N_freqs, self.N_times)).T
+                   try:
+                     gain_amplitude = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                   except KeyError:
+                     #self.amplitudes[:, sorted_freq_selection, station_idx, :, pol_idx] = numpy.ones((self.N_times, N_freqs, self.N_sources))
+                     pass
+                   else:
+                     self.amplitudes[:, sorted_freq_selection, station_idx, :, pol_idx] = abs(numpy.resize(gain_amplitudes.T, (self.N_sources, N_freqs, self.N_times)).T)
+                     self.phases[:, sorted_freq_selection, station_idx, :, pol_idx] += angle(numpy.resize(gain_amplitudes.T, (self.N_sources, N_freqs, self.N_times)).T)
+                 else:
+                   gain_real = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                   gain_imag = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                   self.phases[:, sorted_freq_selection, station_idx, :, pol_idx] = numpy.resize(numpy.arctan2(gain_imag.T, gain_real.T),(self.N_sources, N_freqs, self.N_times)).T
+                   self.amplitudes[:, sorted_freq_selection, station_idx, :, pol_idx] = numpy.resize(numpy.sqrt(gain_imag.T**2 + gain_real.T**2),(self.N_sources, N_freqs, self.N_times)).T
+              if self.DirectionalGainEnable:
+                for source, source_idx in zip(self.sources, list(range(len(self.sources)))) :
+                  parmname0 = ':'.join(['DirectionalGain', str(pol), str(pol), infix[0], station, source])
+                  parmname1 = ':'.join(['DirectionalGain', str(pol), str(pol), infix[1], station, source])
+                  if self.PhasorsEnable:
+                    gain_phase = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                    self.phases[:, sorted_freq_selection, station_idx, source_idx, pol_idx] += gain_phase
+                    try:
+                      gain_amplitude = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                    except KeyError:
+                      pass
+                    else:
+                      self.amplitudes[:, sorted_freq_selection, station_idx, source_idx, pol_idx] *= abs(gain_amplitude)
+                      self.phases[:, sorted_freq_selection, station_idx, source_idx, pol_idx] += angle(gain_amplitude)
+                  else:
+                    gain_real = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                    gain_imag = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                    l = min(gain_real.shape[0], gain_imag.shape[0], self.phases.shape[0])
+                    if l != self.phases.shape[0]:
+                      shapemismatch = True
+                    gain_real = gain_real[0:l,:]
+                    gain_imag = gain_imag[0:l,:]
+                    self.phases[0:l, sorted_freq_selection, station_idx, source_idx, pol_idx]  += numpy.arctan2(gain_imag, gain_real)
+                    self.amplitudes[0:l, sorted_freq_selection, station_idx, source_idx, pol_idx] *= numpy.sqrt(gain_real**2 + gain_imag**2)
+         if self.RotationEnable:
+           for station, station_idx in zip(self.stations, list(range(len(self.stations)))):
+             for source, source_idx in zip(self.sources, list(range(len(self.sources)))) :
+               parmname = ':'.join(['RotationAngle', station, source])
+               rotation = instrumentdb.getValuesGrid( parmname )[ parmname ]['values']
+               l = min(rotation.shape[0], self.rotation.shape[0])
+               if l != self.rotation.shape[0] :
+                 shapemismatch = True
+               self.rotation[:l, sorted_freq_selection, station_idx, source_idx] = rotation[:l,:]
+         freq_idx += N_freqs
+         print(["","*"][shapemismatch])
+
+      if self.flags.shape !=  self.phases.shape[0:2] :
+         self.flags = numpy.zeros(self.phases.shape[0:2])
+         
+   def calculate_piercepoints(self, time_steps = [], height = 200.e3):
+      if ( len( time_steps ) == 0 ):
+         n_list = list(range( self.times.shape[0]))
+      else:
+         n_list = time_steps
+      self.n_list = n_list
+      if 'n_list' in self.hdf5.root: self.hdf5.root.n_list.remove()
+      self.hdf5.createArray(self.hdf5.root, 'n_list', self.n_list)
+
+      self.height = height
+
+      if 'piercepoints' in self.hdf5.root: self.hdf5.root.piercepoints.remove()
+      description = {'positions':tables.Float64Col((self.N_sources, self.N_stations,2)), \
+                     'positions_xyz':tables.Float64Col((self.N_sources, self.N_stations,3)), \
+                     'zenith_angles':tables.Float64Col((self.N_sources, self.N_stations))}
+      self.piercepoints = self.hdf5.createTable(self.hdf5.root, 'piercepoints', description)
+      self.piercepoints.attrs.height = self.height
+      piercepoints_row = self.piercepoints.row
+      p = ProgressBar(len(n_list), "Calculating piercepoints: ")
+      for (n, counter) in zip(n_list, list(range(len(n_list)))):
+         p.update(counter)
+         piercepoints =  PiercePoints( self.times[ n ], self.pointing, self.array_center, self.source_positions, self.station_positions, height = self.height )
+         piercepoints_row['positions'] = piercepoints.positions
+         piercepoints_row['positions_xyz'] = piercepoints.positions_xyz
+         piercepoints_row['zenith_angles'] = piercepoints.zenith_angles
+         piercepoints_row.append()
+      self.piercepoints.flush()
+      p.finished()
+         
+   def calculate_basevectors(self, order = 15, beta = 5. / 3., r_0 = 1.):
+      self.order = order
+      self.beta = beta
+      self.r_0 = r_0
+      
+      N_stations = len(self.stations)
+      N_sources = len(self.sources)
+      
+      N_piercepoints = N_stations * N_sources
+      P = eye(N_piercepoints) - ones((N_piercepoints, N_piercepoints)) / N_piercepoints
+      
+      self.C_list = []
+      self.U_list = []
+      self.S_list = []
+      p = ProgressBar(len(self.piercepoints), "Calculating base vectors: ")
+      for (piercepoints, counter) in zip(self.piercepoints, list(range(len(self.piercepoints)))):
+         p.update( counter )
+         Xp_table = reshape(piercepoints['positions_xyz'], (N_piercepoints, 3) )
+         
+         # calculate structure matrix
+         D = resize( Xp_table, ( N_piercepoints, N_piercepoints, 3 ) )
+         D = transpose( D, ( 1, 0, 2 ) ) - D
+         D2 = sum( D**2, 2 )
+         C = -(D2 / ( r_0**2 ) )**( beta / 2.0 )/2.0
+         self.C_list.append(C)
+
+         # calculate covariance matrix C
+         # calculate partial product for interpolation B
+         # reforce symmetry
+         
+         C = dot(dot(P, C ), P)
+
+         # eigenvalue decomposition
+         # reforce symmetry
+         # select subset of base vectors
+         [ U, S, V ] = linalg.svd( C )
+         U = U[ :, 0 : order ]
+         S = S[ 0 : order ]
+         self.U_list.append(U) 
+         self.S_list.append(S)
+      p.finished()
+
+   def fit_model  ( self ) :
+      N_stations = len(self.stations)
+      N_times = len(self.times)
+      N_sources = len(self.sources)
+      N_pol = len(self.polarizations)
+      G = kron(eye( N_sources ), ( eye( N_stations ) - ones((N_stations, N_stations)) / N_stations))
+
+      if 'TECfit' in self.hdf5.root: self.hdf5.root.TECfit.remove()
+      self.TECfit = self.hdf5.createArray(self.hdf5.root, 'TECfit', zeros(self.TEC.shape))
+      
+      if 'TECfit_white' in self.hdf5.root: self.hdf5.root.TECfit_white.remove()
+      self.TECfit_white = self.hdf5.createArray(self.hdf5.root, 'TECfit_white', zeros(self.TEC.shape))
+      
+      self.offsets = zeros((N_pol, len(self.n_list)))
+      p = ProgressBar(len(self.n_list), "Fitting phase screen: ")
+      for i in range(len(self.n_list)) :
+         p.update( i )
+         U = self.U_list[i]
+         S = self.S_list[i]
+         for pol in range(1) : # range(N_pol) :
+            TEC = self.TEC[ pol, self.n_list[i], :, :].reshape( (N_sources * N_stations, 1) )
+            TECfit = dot(U, dot(inv(dot(U.T, dot(G, U))), dot(U.T, dot(G, TEC))))
+            TECfit_white = dot(U, dot(diag(1/S), dot(U.T, TECfit)))
+            self.offsets[pol, i] = TECfit[0] - dot(self.C_list[i][0,:], TECfit_white)
+            self.TECfit[ pol, i, :, : ] = reshape( TECfit, (N_sources, N_stations) )
+            self.TECfit_white[ pol, i, :, : ] = reshape( TECfit_white, (N_sources, N_stations) )
+      p.finished()      
+
+      self.TECfit.attrs.r_0 = self.r_0
+      self.TECfit.attrs.beta = self.beta
+
+      self.TECfit_white.attrs.r_0 = self.r_0
+      self.TECfit_white.attrs.beta = self.beta
+      
+      if 'offsets' in self.hdf5.root: self.hdf5.root.offsets.remove()
+      self.hdf5.createArray(self.hdf5.root, 'offsets', self.offsets)
+
+   def make_movie( self, extent = 0, npixels = 100, vmin = 0, vmax = 0 ):
+      """
+      """
+
+      multiengine_furl =  os.environ['HOME'] + '/ipcluster/multiengine.furl'
+#      mec = client.MultiEngineClient( multiengine_furl )
+      mec = client.MultiEngineClient( )
+      task_furl =  os.environ['HOME'] + '/ipcluster/task.furl'
+      #tc = client.TaskClient( task_furl )
+      tc = client.TaskClient( )
+      N_stations = len(self.stations)
+      N_times = self.TECfit.shape[1]
+      N_sources = len(self.sources)
+      N_piercepoints = N_stations * N_sources
+      N_pol = self.TECfit_white.shape[0]
+      R = 6378137.0
+      taskids = []
+
+      beta = self.TECfit.attrs.beta
+      r_0 = self.TECfit.attrs.r_0
+      
+      print("Making movie...")
+      p = ProgressBar( len( self.n_list ), 'Submitting jobs: ' )
+      for i in range(len(self.n_list)) :
+         p.update(i)
+         Xp_table = reshape(self.piercepoints[i]['positions'], (N_piercepoints, 2) )
+         if extent > 0 :
+            w = extent/R/2
+         else :
+            w = 1.1*abs(Xp_table).max()
+         for pol in range(N_pol) :
+            v = self.TECfit[ pol, i, :, : ].reshape((N_piercepoints,1))
+            maptask = client.MapTask(calculate_frame, (Xp_table, v, beta, r_0, npixels, w) )
+            taskids.append(tc.run(maptask))
+      p.finished()
+      
+      if vmin == 0 and vmax == 0 :
+         vmin = self.TECfit.min()
+         vmax = self.TECfit.max()
+         vdiff = vmax - vmin
+         vmin = vmin - 0.1*vdiff
+         vmax = vmax + 0.1*vdiff
+
+      p = ProgressBar( len( self.n_list ), 'Fetch results: ' )
+      for i in range(len(self.n_list)) :
+         p.update(i)
+         clf()
+         for pol in range(N_pol) :
+            (phi,w) = tc.get_task_result(taskids.pop(0), block = True)
+            phi = phi + self.offsets[pol, i]
+            subplot(1, N_pol, pol+1)
+            w = w*R*1e-3
+            h_im = imshow(phi, interpolation = 'nearest', origin = 'lower', extent = (-w, w, -w, w), vmin = vmin, vmax = vmax )
+            h_axes = gca()
+            cl = h_im.get_clim()
+            TEC =  reshape( self.TEC[ pol, i, :, : ], N_piercepoints )
+            for j in range(N_piercepoints):
+               color = h_im.cmap(int(round((TEC[j]-cl[0])/(cl[1]-cl[0])*(h_im.cmap.N-1))))
+               plot(R*1e-3*Xp_table[j,0], R*1e-3*Xp_table[j,1], marker = 'o', markeredgecolor = 'k', markerfacecolor = color)
+            colorbar()
+            xlim(-w, w)
+            ylim(-w, w)
+         savefig('tmpfig%4.4i.png' % i)
+      p.finished()
+      os.system("mencoder mf://tmpfig????.png -o movie.mpeg -mf type=png:fps=3  -ovc lavc -ffourcc DX50 -noskip -oac copy")
+
+   def interpolate( self, facetlistfile ) :
+      
+      """
+      """
+      
+      #facetdbname = os.path.join(self.globaldb, 'facets')
+      #os.system( 'makesourcedb in=%s out=%s append=False' % (facetlistfile, facetdbname) )
+      
+      #patch_table = pt.table( os.path.join(facetdbname, 'SOURCES', 'PATCHES' ) )
+      
+      #if 'facets' in self.hdf5.root: self.hdf5.root.facets.remove()
+      #description = {'name': tables.StringCol(40), 'position':tables.Float64Col(2)}
+      #self.facets = self.hdf5.createTable(self.hdf5.root, 'facets', description)
+
+      #facet = self.facets.row
+      #for patch in patch_table :
+         #facet['name'] = patch['PATCHNAME']
+         #facet['position'] = array([patch['RA'], patch['DEC']])
+         #facet.append()
+      #self.facets.flush()
+      self.N_facets = len(self.facets)
+      
+      self.facet_names = self.facets[:]['name']
+      self.facet_positions = self.facets[:]['position']
+
+      print(self.n_list)
+      if 'STEC_facets' in self.hdf5.root: self.hdf5.root.STEC_facets.remove()
+      self.STEC_facets = self.hdf5.createCArray(self.hdf5.root, 'STEC_facets', tables.Float32Atom(), shape = (self.N_pol, self.n_list.shape[0],  self.N_facets, self.N_stations))
+
+      #if 'facet_piercepoints' in self.hdf5.root: self.hdf5.root.facet_piercepoints.remove()
+      #description = {'positions':tables.Float64Col((self.N_facets, self.N_stations,2)), \
+                     #'positions_xyz':tables.Float64Col((self.N_facets, self.N_stations,3)), \
+                     #'zenith_angles':tables.Float64Col((self.N_facets, self.N_stations))}
+      #self.facet_piercepoints = self.hdf5.createTable(self.hdf5.root, 'facet_piercepoints', description)
+      #height = self.piercepoints.attrs.height
+      #facet_piercepoints_row = self.facet_piercepoints.row
+      #print "Calculating facet piercepoints..."
+      #for n in self.n_list:
+         #piercepoints = PiercePoints( self.times[ n ], self.pointing, self.array_center, self.facet_positions, self.station_positions, height = height )
+         #facet_piercepoints_row['positions'] = piercepoints.positions
+         #facet_piercepoints_row['positions_xyz'] = piercepoints.positions_xyz
+         #facet_piercepoints_row['zenith_angles'] = piercepoints.zenith_angles
+         #facet_piercepoints_row.append()
+      #self.facet_piercepoints.flush()
+
+      r_0 = self.TECfit_white.attrs.r_0
+      beta = self.TECfit_white.attrs.beta
+      
+      for facet_idx in range(self.N_facets) :
+         for station_idx in range(self.N_stations):
+            for pol_idx in range(self.N_pol) :
+               TEC_list = []
+               for n in range(len(self.n_list)):
+                  p = self.facet_piercepoints[n]['positions_xyz'][facet_idx, station_idx,:]
+                  za = self.facet_piercepoints[n]['zenith_angles'][facet_idx, station_idx]
+                  Xp_table = reshape(self.piercepoints[n]['positions_xyz'], (self.N_piercepoints, 3) )
+                  v = self.TECfit_white[ pol_idx, n, :, : ].reshape((self.N_piercepoints,1))
+                  D2 = sum((Xp_table - p)**2,1)
+                  C = (D2 / ( r_0**2 ) )**( beta / 2. ) / -2.
+                  self.STEC_facets[pol_idx, n,  facet_idx, station_idx] = dot(C, v)/cos(za)
+
+   def ClockTEC( self, ClockEnable = True, TECEnable = True, stations = []) :
+      """
+      Estimate Clock and ionospheric TEC from phase information 
+      """
+
+      if not ClockEnable and not TECEnable: return
+      
+      if len(stations) == 0:
+         stations = self.stations
+      
+      station_list = list(self.stations[:])
+      station_idx = [station_list.index(station) for station in stations]
+      N_stations = len(stations)
+      N_baselines = N_stations * (N_stations - 1) / 2
+      time_start = 3000
+      time_end = 6000
+      N_times = time_end - time_start
+
+      N_times = len(self.times)
+      time_start = 0
+      time_end = N_times
+      
+      N_sources = len(self.sources)
+      N_pol = len(self.polarizations)
+      
+      if N_sources == 0:
+         N_sources = 1
+
+      if ClockEnable:
+         if 'Clock' in self.hdf5.root: self.hdf5.root.Clock.remove()
+         self.Clock = self.hdf5.createCArray(self.hdf5.root, 'Clock', tables.Float32Atom(), shape = (N_pol, N_times, N_stations))
+      if TECEnable:
+         if 'TEC' in self.hdf5.root: self.hdf5.root.TEC.remove()
+         self.TEC = self.hdf5.createCArray(self.hdf5.root, 'TEC', tables.Float32Atom(), shape = (N_pol, N_times,  N_sources, N_stations))
+      
+      
+      for pol in range(N_pol):
+         for source_no in range(N_sources):
+            if ClockEnable and TECEnable:
+               (Clock, TEC) = fit_Clock_and_TEC( squeeze( self.phases[time_start:time_end, :, station_idx, source_no, pol] ),
+                                            self.freqs[:], self.flags[time_start:time_end, :] )
+               self.Clock[ pol, :, 1: ] = Clock
+               self.TEC[ pol, :, source_no, 1: ] = TEC
+            else : 
+               v = fit_Clock_or_TEC( squeeze( self.phases[:, :, station_idx, source_no, pol] ), self.freqs[:], self.flags[:, :], ClockEnable )
+               if ClockEnable :
+                  self.Clock[ pol, :, source_no, 1: ] = v
+               else:
+                  self.TEC[ pol, :, source_no, 1: ] = v
+            
+   def write_to_parmdb( self ) : 
+      """
+      Write Clock and TEC to a parmdb
+      """
+
+      N_sources = len(self.sources)
+      
+      if N_sources == 0:
+         N_sources = 1
+
+      parms = {}
+      parm = {}
+      parm[ 'freqs' ] = numpy.array( [ .5e9 ] )
+      parm[ 'freqwidths' ] = numpy.array( [ 1.0e9 ] )
+      parm[ 'times' ] = self.times[:].ravel()
+      parm[ 'timewidths' ] = self.timewidths[:].ravel()
+
+      for n_pol in range(len(self.polarizations)):
+         pol = self.polarizations[n_pol]
+
+         for n_station in range(len(self.stations)):
+            station = self.stations[n_station]
+            
+            # Clock
+            if 'Clock' in self.__dict__ :
+               Clock_parm = parm.copy()
+               parmname = ':'.join(['Clock', str(pol), station])
+               Clock_parm[ 'values' ] = self.Clock[n_pol, :, n_station]
+               parms[ parmname ] = Clock_parm
+
+            for n_source in range(N_sources):
+               if self.DirectionalGainEnable:
+                  source = self.sources[n_source]
+                  identifier = ':'.join([str(pol), station, source])
+               else:
+                  identifier = ':'.join([str(pol), station])
+               
+               # TEC
+               TEC_parm = parm.copy()
+               parmname = ':'.join(['TEC', identifier])
+               TEC_parm[ 'values' ] = self.TEC[n_pol, :, n_source, n_station]
+               parms[ parmname ] = TEC_parm
+               
+               #TECfit
+               TECfit_parm = parm.copy()
+               parmname = ':'.join(['TECfit', identifier])
+               TECfit_parm[ 'values' ] = self.TECfit[n_pol, :, n_source, n_station]
+               parms[ parmname ] = TECfit_parm
+               
+               #TECfit_white
+               TECfit_white_parm = parm.copy()
+               parmname = ':'.join(['TECfit_white', identifier])
+               TECfit_white_parm[ 'values' ] = self.TECfit_white[n_pol, :, n_source, n_station]
+               parms[ parmname ] = TECfit_white_parm
+               
+      #Piercepoints
+      
+      for n_station in range(len(self.stations)):
+         station = self.stations[n_station]
+         for n_source in range(N_sources):
+            if self.DirectionalGainEnable:
+               source = self.sources[n_source]
+               identifier = ':'.join([station, source])
+            else:
+               identifier = station
+            PiercepointX_parm = parm.copy()
+            parmname = ':'.join(['Piercepoint', 'X', identifier])
+            print(n_source, n_station)
+            x = self.piercepoints[:]['positions_xyz'][:,n_source, n_station,0]
+            PiercepointX_parm['values'] = x
+            parms[ parmname ] = PiercepointX_parm
+
+            PiercepointY_parm = parm.copy()
+            parmname = ':'.join(['Piercepoint', 'Y', identifier])
+            y = self.piercepoints[:]['positions_xyz'][:,n_source, n_station,1]
+            PiercepointY_parm['values'] = array(y)
+            parms[ parmname ] = PiercepointY_parm
+
+            PiercepointZ_parm = parm.copy()
+            parmname = ':'.join(['Piercepoint', 'Z', identifier])
+            z = self.piercepoints[:]['positions_xyz'][:,n_source, n_station,2]
+            PiercepointZ_parm['values'] = z
+            parms[ parmname ] = PiercepointZ_parm
+
+            Piercepoint_zenithangle_parm = parm.copy()
+            parmname = ':'.join(['Piercepoint', 'zenithangle', identifier])
+            za = self.piercepoints[:]['zenith_angles'][:,n_source, n_station]
+            Piercepoint_zenithangle_parm['values'] = za
+            parms[ parmname ] = Piercepoint_zenithangle_parm
+
+      time_start = self.times[0] - self.timewidths[0]/2
+      time_end = self.times[-1] + self.timewidths[-1]/2
+      
+      
+      parm[ 'times' ] = numpy.array([(time_start + time_end) / 2])
+      parm[ 'timewidths' ] = numpy.array([time_end - time_start])
+
+      height_parm = parm.copy()
+      height_parm[ 'values' ] = numpy.array( self.piercepoints.attrs.height )
+      parms[ 'height' ] = height_parm
+
+      beta_parm = parm.copy()
+      beta_parm[ 'values' ] = numpy.array( self.TECfit_white.attrs.beta )
+      parms[ 'beta' ] = beta_parm
+      
+      r_0_parm = parm.copy()
+      r_0_parm[ 'values' ] = numpy.array(  self.TECfit_white.attrs.r_0 )
+      parms[ 'r_0' ] = r_0_parm
+      
+      parmdbmain.store_parms( self.globaldb + '/ionosphere', parms, create_new = True)
+
+   def write_phases_to_parmdb( self, gdsfile, phases_name = 'ionosphere') :
+      gdsparset = lofar.parameterset.parameterset( gdsfile )
+      N_parts = gdsparset.getInt("NParts")
+      parm = {}
+      N_times = len(self.times)
+      parm[ 'times' ] = self.times[:].ravel()
+      parm[ 'timewidths' ] = self.timewidths[:].ravel()
+      for i in [0]: # range(N_parts):
+         parms = {}
+         msname = gdsparset.getString( "Part%i.FileName" % i )
+         mshostname = gdsparset.getString( "Part%i.FileSys" % i).split(':')[0]
+         spectral_table_name = self.globaldb + "/SPECTRAL_WINDOW"
+         if not os.path.exists( spectral_window_name ) :
+            os.system("scp -r %s:%s/SPECTRAL_WINDOW %s" % ( mshostname, msname, spectral_window_name ))
+         spectral_table = pt.table( spectral_table_name )
+         freqs = spectral_table[0]['CHAN_FREQ']
+         N_freqs = len(freqs)
+         parm[ 'freqs' ] = freqs
+         parm[ 'freqwidths' ] = spectral_table[0]['CHAN_WIDTH']
+         spectral_table.close()
+
+         for (pol, pol_idx) in zip(self.polarizations, list(range(len(self.polarizations)))):
+            for n_station in range(len(self.stations)):
+               station = self.stations[n_station]
+               
+               ## Clock
+               #if 'Clock' in self.__dict__ :
+                  #Clock_parm = parm.copy()
+                  #parmname = ':'.join(['Clock', str(pol), station])
+                  #Clock_parm[ 'values' ] = self.Clock[n_pol, :, n_station]
+                  #parms[ parmname ] = Clock_parm
+
+               for (facet, facet_idx) in zip(self.facets[:]['name'], list(range(len(self.facets)))):
+                  v = exp(1j * self.STEC_facets[pol_idx, :, facet_idx, n_station].reshape((N_times,1)) * \
+                      8.44797245e9 / freqs.reshape((1, N_freqs)))
+                  identifier = ':'.join([str(pol), str(pol), 'Real', station, facet])
+                  DirectionalGain_parm = parm.copy()
+                  parmname = ':'.join(['DirectionalGain', identifier])
+                  DirectionalGain_parm[ 'values' ] = real(v)
+                  parms[ parmname ] = DirectionalGain_parm
+
+                  identifier = ':'.join([str(pol), str(pol), 'Imag', station, facet])
+                  DirectionalGain_parm = parm.copy()
+                  parmname = ':'.join(['DirectionalGain', identifier])
+                  DirectionalGain_parm[ 'values' ] = imag(v)
+                  parms[ parmname ] = DirectionalGain_parm
+                  
+         parmdbmain.store_parms( self.globaldb + '/facetgains', parms, create_new = True)
+         
+   #def write_phases_to_parmdb( self, gdsfiles = [], phases_name = 'ionosphere') :
+      #if len(gdsfiles) == 0:
+         #gdsfiles = self.gdsfiles
+      #for gdsfile in gdsfiles :
+         #instrument_parmdbname = os.path.splitext(gdsfile)[0] + os.path.extsep + str(self.instrument_name)
+         #phases_parmdbname = os.path.splitext(gdsfile)[0] + os.path.extsep + phases_name
+         #os.system( "rundist -wd %s parmdbwriter.py %s %s %s" % (os.environ['PWD'], instrument_parmdbname, self.globaldb, phases_name) )
+
+         #p = re.compile('(^Part\\d*.FileName\\s*=\\s*\\S*)(%s$)' % str(self.instrument_name))
+         #print repr(instrument_parmdbname)
+         #file_instrument_parmdb = open(instrument_parmdbname)
+         #file_phases_parmdb = open(phases_parmdbname, 'w')
+         #file_phases_parmdb.writelines([p.sub('\\1%s' % phases_name, l) for l in file_instrument_parmdb.readlines()])
+         #file_instrument_parmdb.close()
+         #file_phases_parmdb.close()
+
+def product(*args, **kwds):
+    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+    pools = list(map(tuple, args)) * kwds.get('repeat', 1)
+    result = [[]]
+    for pool in pools:
+        result = [x+[y] for x in result for y in pool]
+    for prod in result:
+        yield tuple(prod)
+
+def fillarray( a, v ) :
+   print(a.shape, a.chunkshape)
+   for idx in product(*[range(0, s, c) for s, c in zip(a.shape, a.chunkshape)]) :
+      s = tuple([slice(i,min(i+c,s)) for i,s,c in zip(idx, a.shape, a.chunkshape)])
+      a[s] = v
+   
+
+
+def calculate_frame(Xp_table, v, beta, r_0, npixels, w):
+  import numpy
+  
+  N_piercepoints = Xp_table.shape[0]
+  P = numpy.eye(N_piercepoints) - numpy.ones((N_piercepoints, N_piercepoints)) / N_piercepoints
+  # calculate structure matrix
+  D = numpy.resize( Xp_table, ( N_piercepoints, N_piercepoints, 2 ) )
+  D = numpy.transpose( D, ( 1, 0, 2 ) ) - D
+  D2 = numpy.sum( D**2, 2 )
+  C = -(D2 / ( r_0**2 ) )**( beta / 2.0 )/2.0
+  C = numpy.dot(numpy.dot(P, C ), P)
+  v = numpy.dot(numpy.linalg.pinv(C), v)
+   
+  phi = numpy.zeros((npixels,npixels))
+  for x_idx in range(0, npixels):
+    x = -w + 2*x_idx*w/( npixels-1 )  
+    for y_idx in range(0, npixels):
+      y = -w + 2*y_idx*w/(npixels-1)
+      D2 = numpy.sum((Xp_table - numpy.array([ x, y ]))**2,1)
+      C = (D2 / ( r_0**2 ) )**( beta / 2.0 ) / -2.0
+      phi[y_idx, x_idx] = numpy.dot(C, v)
+  return phi, w
+
+def fit_Clock_or_TEC( phase, freqs, flags, ClockEnable ):
+   
+   if ClockEnable :
+      v = freqs.reshape((freqs.size,1))*2*pi
+   else :
+      v = 8.44797245e9/freqs.reshape((freqs.size,1))
+      
+   A1 = numpy.concatenate([v, 2*pi*numpy.ones(v.shape)], axis=1)
+   A2 = v
+
+   p22 = []
+   rr = []
+   
+   multiengine_furl =  os.environ['HOME'] + '/ipcluster/multiengine.furl'
+   #mec = client.MultiEngineClient( multiengine_furl )
+   mec = client.MultiEngineClient( )
+   mec.execute('import numpy, scipy.optimize')
+
+   task_furl =  os.environ['HOME'] + '/ipcluster/task.furl'
+   #tc = client.TaskClient( task_furl )
+   tc = client.TaskClient( )
+
+   taskids = []
+   for i in range(0,phase.shape[0]):
+      print(i+1, '/', phase.shape[0])
+      maptask = client.MapTask(fit_Clock_or_TEC_worker, ( phase[i, :, :], flags[i, :], A1,A2))
+      taskids.append(tc.run(maptask))
+   i = 0
+   for taskid in taskids :
+      i += 1
+      print(i, '/', len(taskids))
+      (residual_std, p) = tc.get_task_result(taskid, block = True)
+      rr.append(residual_std)
+      p22.append(p)      
+      
+   Clock_or_TEC = numpy.array(p22)
+
+   tc.clear()
+   del tc
+   del mec
+   
+   return Clock_or_TEC
+
+
+####################################################################
+def fit_Clock_or_TEC_worker( phase, flags, A1, A2 ):
+   
+   def costfunction(p, A, y, flags = 0) : 
+      N_stations = y.shape[1]
+      TEC = numpy.concatenate( ( numpy.zeros(1), p ) )
+      e = []
+      for station0 in range(0, N_stations):
+         for station1 in range(station0 + 1, N_stations):
+            p1 = TEC[station1] - TEC[station0]
+            dphase = y[:, [station1]] - y[:, [station0]]
+            e.append( (numpy.mod( numpy.dot(A, p1) - dphase + numpy.pi, 2*numpy.pi ) - numpy.pi) )
+      e = numpy.concatenate(e, axis=0)
+      e = e[:,0] * (1-flags)
+      return e
+  
+   N_stations = phase.shape[1]
+   
+   rr = []
+   A = []
+   dphase = []
+   flags1 = []
+   not_flagged_idx, = numpy.nonzero(1-flags)
+   for station0 in range(0, N_stations):
+      for station1 in range(station0 + 1, N_stations):
+         v = numpy.zeros(N_stations)
+         v[station1] = 1
+         v[station0] = -1
+         A.append(v)
+         dphase1 = numpy.zeros(phase.shape[0])
+         dphase1[not_flagged_idx] = numpy.unwrap(phase[not_flagged_idx, station1] - phase[not_flagged_idx, station0])
+         dphase.append(dphase1)
+         flags1.append(flags)
+   A = numpy.array(A)
+   dphase = numpy.concatenate(dphase)
+   flags1 = numpy.concatenate(flags1)
+   
+   A3 = numpy.kron(A[:,1:], A1)
+   S3 = numpy.dot(numpy.linalg.inv(numpy.dot(A3.T, A3)), A3.T)
+   p =  numpy.dot(S3, dphase)
+   
+   p[0::2] = 0
+   p[1::2] = numpy.round(p[1::2])
+   dphase = dphase - numpy.dot(A3, p)
+   
+   A4 = numpy.kron(A[:,1:], A2)
+   S4 = numpy.dot(numpy.linalg.inv(numpy.dot(A4.T, A4)), A4.T)
+   p = numpy.dot(S4, dphase)
+   
+   p, returncode = scipy.optimize.leastsq(costfunction, p, (A2, phase, flags1))
+   
+   while True:
+      dphase_fit = numpy.dot(A4, p)
+      residual = (numpy.mod(dphase - dphase_fit + numpy.pi,2*numpy.pi) - numpy.pi)
+      residual_std = numpy.sqrt(numpy.mean(residual[flags1==0]**2))
+      new_outlier_idx, = numpy.nonzero( (numpy.abs(residual) > (3*residual_std)) & (flags1 == 0))
+      if len(new_outlier_idx) == 0:
+         break
+      flags1[new_outlier_idx] = 1
+      p, returncode = scipy.optimize.leastsq(costfunction, p, (A2, phase, flags1))
+      p_init = p
+      
+   return residual_std, p
+
+####################################################################
+
+def fit_Clock_and_TEC( phase, freqs, flags ):
+
+   A1 = zeros((len(freqs),3))
+   A1[:,0] = freqs*2*pi
+   A1[:,1] = 8.44797245e9/freqs
+   A1[:,2] = 2*pi*numpy.ones(freqs.shape)
+
+   A2 = A1[:, 0:2]
+   S2 = numpy.dot(numpy.linalg.inv(numpy.dot(A2.T, A2)), A2.T)
+
+   dp = 2*pi*numpy.dot(S2, ones(phase.shape[1]))
+
+   p22 = []
+   residual_std1 = []
+
+   rr = []
+   multiengine_furl =  os.environ['HOME'] + '/ipcluster/multiengine.furl'
+   #mec = client.MultiEngineClient( multiengine_furl )
+   mec = client.MultiEngineClient( )
+   mec.execute('import numpy, scipy.optimize')
+
+   
+   task_furl =  os.environ['HOME'] + '/ipcluster/task.furl'
+   #tc = client.TaskClient( task_furl )
+   tc = client.TaskClient(  )
+
+   taskids = []
+   for i in range(0,phase.shape[0]):
+      print(i+1, '/', phase.shape[0])
+      sys.stdout.flush()
+      maptask = client.MapTask(fit_Clock_and_TEC_worker, ( phase[i, :, :], flags[i, :], A1,A2,dp))
+      taskids.append(tc.run(maptask))
+   i = 0
+   for taskid in taskids :
+      i += 1
+      print(i, '/', len(taskids))
+      sys.stdout.flush()
+      (residual_std, p) = tc.get_task_result(taskid, block = True)
+      rr.append(residual_std)
+      p22.append(p.copy())      
+   rr = numpy.array(rr)
+   p22 = numpy.array(p22)
+
+   tc.clear()
+   del tc
+   del mec
+
+   Clock = p22[:,0::2]
+   TEC = p22[:,1::2]
+
+   return (Clock, TEC)
+
+####################################################################
+
+def fit_Clock_and_TEC1( phase, freqs, flags ):
+
+   A1 = zeros((len(freqs),3))
+   A1[:,0] = freqs*2*pi
+   A1[:,1] = 8.44797245e9/freqs
+   A1[:,2] = 2*pi*numpy.ones(freqs.shape)
+
+   A2 = A1[:, 0:2]
+   S2 = numpy.dot(numpy.linalg.inv(numpy.dot(A2.T, A2)), A2.T)
+
+   dp = 2*pi*numpy.dot(S2, ones(phase.shape[1]))
+
+   p22 = []
+   residual_std1 = []
+
+   rr = []
+   for i in range(0,phase.shape[0]):
+      print(i+1, '/', phase.shape[0])
+      sys.stdout.flush()
+      residual_std, p = fit_Clock_and_TEC_worker ( phase[i, :, :], flags[i, :], A1,A2,dp)
+      rr.append(residual_std)
+      p22.append(p.copy())      
+   rr = numpy.array(rr)
+   p22 = numpy.array(p22)
+
+   Clock = p22[:,0::2]
+   TEC = p22[:,1::2]
+
+   return (Clock, TEC)
+
+###############################################################################
+
+def fit_Clock_and_TEC_worker( phase, flags, A1, A2, dp ):
+   #import numpy
+   #import scipy.optimize
+   
+   def costfunction(p, A, y, flags = 0) : 
+      N_stations = y.shape[1]
+      Clock = numpy.concatenate( ( numpy.zeros(1), p[0::2] ) )
+      TEC = numpy.concatenate( ( numpy.zeros(1), p[1::2] ) )
+      e = []
+      for station0 in range(0, N_stations):
+         for station1 in range(station0 + 1, N_stations):
+            dClock = Clock[station1] - Clock[station0]
+            dTEC = TEC[station1] - TEC[station0]
+            p1 = numpy.array( [ dClock, dTEC ] )
+            dphase = y[:, station1] - y[:, station0]
+            e.append( (numpy.mod( numpy.dot(A, p1) - dphase + numpy.pi, 2*numpy.pi ) - numpy.pi) )
+      e = numpy.concatenate(e) * (1-flags)
+      return e
+   
+   N_stations = phase.shape[1]
+
+   p_init = numpy.zeros( 2*N_stations -2 )
+   A = []
+   dphase = []
+   flags1 = []
+   not_flagged_idx, = numpy.nonzero(1-flags)
+   for station0 in range(0, N_stations):
+      for station1 in range(station0 + 1, N_stations):
+         v = numpy.zeros(N_stations)
+         v[ station1 ] = 1
+         v[ station0 ] = -1
+         A.append(v)
+         dphase1 = numpy.zeros(phase.shape[0])
+         dphase1[not_flagged_idx] = numpy.unwrap(phase[not_flagged_idx, station1] - phase[not_flagged_idx, station0])
+         dphase.append(dphase1)
+         flags1.append(flags)
+   A = numpy.array(A)
+   dphase = numpy.concatenate(dphase)
+   flags1 = numpy.concatenate(flags1)
+   
+   A3 = numpy.kron(A[:,1:], A1)
+   S3 = numpy.dot(numpy.linalg.inv(numpy.dot(A3.T, A3)), A3.T)
+   p =  numpy.dot(S3, dphase)
+   
+   p[0::3] = 0
+   p[1::3] = 0
+   p[2::3] = numpy.round(p[2::3])
+   dphase = dphase - numpy.dot(A3, p)
+   
+   
+   A4 = numpy.kron(A[:,1:], A2)
+   S4 = numpy.dot(numpy.linalg.inv(numpy.dot(A4.T, A4)), A4.T)
+   p = numpy.dot(S4, dphase)
+   p = p - numpy.kron(numpy.round(p[1::2] / dp[1]), dp)
+      
+   p, returncode = scipy.optimize.leastsq(costfunction, p, (A2, phase, flags1))
+   p_init = p - numpy.kron(numpy.round(p[1::2] / dp[1]), dp)
+   
+   while True:
+      dphase_fit = numpy.dot(A4, p)
+      residual = numpy.mod(dphase - dphase_fit + numpy.pi,2*numpy.pi) - numpy.pi
+      residual_std = numpy.sqrt( numpy.mean ( residual[flags1==0]**2 ) )
+      new_outlier_idx, = numpy.nonzero( (numpy.abs(residual) > (3*residual_std)) & (flags1 == 0))
+      if len( new_outlier_idx ) == 0:
+         break
+      flags1[new_outlier_idx] = 1
+      p, returncode = scipy.optimize.leastsq(costfunction, p_init, (A2, phase, flags1))
+      p_init = p - numpy.kron(numpy.round(p[1::2] / dp[1]), dp)
+  
+   return (residual_std, p)
+
+#####################################################################
+
+
+def fit_phi_klmap_model( P, U_table = None, pza_table = None, phase_table = None, dojac = None):
+# TODO: calculate penalty terms of MAP estimator
+
+   # handle input parameters
+   if ( ( U_table == None ) or
+        ( pza_table == None ) or 
+        ( phase_table == None ) ):
+      return - 1, None, None
+
+   (antenna_count, source_count) = phase_table.shape
+
+   # calculate phases at puncture points
+   phi_table = dot( U_table, P ) / cos( aradians( pza_table ) )
+   phi_table = phi_table.reshape( (antenna_count, source_count) )
+
+   # calculate chi2 terms
+   chi_list = []
+   for source in range(source_count):
+      for i in range(antenna_count):
+         for j in range(i, antenna_count):
+            chi_list.append( mod( phi_table[i, source] - phi_table[j, source] - phase_table[i, source] + phase_table[j, source] + pi, 2*pi) - pi )
+
+   # make (normalized) chi2 array
+   chi_array = array( chi_list )
+
+   return 0, chi_array, None
+
+###############################################################################
+
+# gradient
+
+def phi_gradient_model( X, p ):
+  phi = dot( X, p )
+  return phi
+
+###############################################################################
+
+def phi_klmap_model( X, Xp_table, B_table, F_table, beta = 5. / 3., r_0 = 1. ):
+# B_table = (1/m)(1T)(C_table)(A_table)
+# F_table = ( Ci_table )( U_table )( P_table )
+
+  # input check
+  if ( len( shape( X ) ) == 1 ):
+    X_table = array( [ X ] )
+  elif ( len( shape( X ) ) == 2 ):
+    X_table = X
+
+  # calculate structure matrix
+  x_count = len( X_table )
+  p_count = len( Xp_table )
+  D_table = transpose( resize( X_table, ( p_count, x_count, 2 ) ), ( 1, 0, 2 ) )
+  D_table = D_table - resize( Xp_table, ( x_count, p_count, 2 ) )
+  D_table = add.reduce( D_table**2, 2 )
+  D_table = ( D_table / ( r_0**2 ) )**( beta / 2. )
+
+  # calculate covariance matrix
+  C_table = - D_table / 2.
+  C_table = transpose( transpose( C_table ) - ( add.reduce( C_table, 1 ) / float( p_count ) ) )
+  C_table = C_table - B_table
+
+  phi = dot( C_table, F_table )
+  phi = reshape( phi, ( x_count ) )
+  if ( len( phi ) == 1 ):
+    phi = phi[ 0 ]
+
+  return phi
+
+def get_source_list( pdb, source_pattern_list ):
+   source_list = []
+   for pattern in source_pattern_list :
+      parmname_list = pdb.getNames( 'DirectionalGain:?:?:*:*:' + pattern )
+      source_list.extend([n.split(':')[-1] for n in parmname_list])
+      parmname_list = pdb.getNames( 'RotationAngle:*:' + pattern )
+      source_list.extend([n.split(':')[-1] for n in parmname_list])
+   print(source_list)
+   return sorted(set(source_list))
+
+def get_source_list_from_ionospheredb( pdb ):
+   parmname_list = pdb.getNames( 'TEC:?:*:*:*' )
+   source_list = [n.split(':')[-1] for n in parmname_list]
+   return sorted(set(source_list))
+
+def get_station_list( pdb, station_pattern_list, DirectionalGainEnable ):
+   station_list = []
+   for pattern in station_pattern_list :
+      parmname_list = pdb.getNames( { True : 'DirectionalGain:?:?:*:'+pattern+':*', False: 'Gain:?:?:*:' + pattern}[DirectionalGainEnable] )
+      station_list.extend(sorted(set([n.split(':')[{True : -2, False : -1}[DirectionalGainEnable]] for n in parmname_list])))
+   return station_list
+
+def get_station_list_from_ionospheredb( pdb ):
+   parmname_list = pdb.getNames( 'TEC:?:*:*:*' )
+   station_list = [n.split(':')[-2] for n in parmname_list]
+   return sorted(set(station_list))
+
+def get_time_list_from_ionospheredb( pdb, pattern = '*' ):
+   parameter_names = pdb.getNames( pattern )
+   parm0 = pdb.getValuesGrid(parameter_names[0])[parameter_names[0]]
+   time_list = parm0['times']
+   time_width_list = parm0['timewidths']
+   return (time_list, time_width_list)
+
+###############################################################################
+
+class PiercePoints:
+   
+   def __init__( self, time, pointing, array_center, source_positions, antenna_positions, height = 400.e3 ):
+      # source table radecs at observing epoch
+
+      # calculate Earth referenced coordinates of puncture points of array center towards pointing center
+      [ center_pxyz, center_pza ] = sphere.calculate_puncture_point_mevius( array_center, pointing, time, height = height )
+      self.center_p_geo_llh = sphere.xyz_to_geo_llh( center_pxyz, time )
+
+      # loop over sources
+      positions = []
+      positions_xyz = []
+      zenith_angles = []
+      
+      for k in range( len( source_positions ) ):
+         positions_xyz1 = []
+         positions1 = []
+         zenith_angles1 = []
+
+         # loop over antennas
+         for i in range( len( antenna_positions ) ):
+            # calculate Earth referenced coordinates of puncture points of antenna towards peeled source
+            [ pxyz, pza ] = sphere.calculate_puncture_point_mevius( antenna_positions[ i ], source_positions[ k ], time, height = height )
+            p_geo_llh = sphere.xyz_to_geo_llh( pxyz, time )
+
+            # calculate local angular coordinates of antenna puncture point ( x = East, y = North )
+            [ separation, angle ] = sphere.calculate_angular_separation( self.center_p_geo_llh[ 0 : 2 ], p_geo_llh[ 0 : 2 ] )
+            X = [ separation * sin( angle ), separation * cos( angle ) ]
+
+            # store model fit input data
+            positions1.append(X)
+            positions_xyz1.append( pxyz )
+            zenith_angles1.append( pza )
+         positions.append(positions1)
+         positions_xyz.append(positions_xyz1)
+         zenith_angles.append( zenith_angles1 )
+      
+      self.positions = array( positions ) 
+      self.positions_xyz = array( positions_xyz ) 
+      self.zenith_angles = array( zenith_angles )
+
+def make_instrumentdb( gdsfilename, instrument_name, globaldb ):
+   instrumentdb_name = os.path.join( globaldb, os.path.splitext(os.path.basename(gdsfilename))[0] + os.path.extsep + instrument_name)
+   p = re.compile('(^Part\\d*.FileName\\s*=\\s*\\S*)')
+   gdsfile = open(gdsfilename)
+   instrumentdb_file = open(instrumentdb_name, 'w')
+   instrumentdb_file.writelines([p.sub('\\1%s%s' % (os.path.sep, instrument_name), l) for l in gdsfile.readlines()])
+   gdsfile.close()
+   instrumentdb_file.close()
+   return instrumentdb_name
+  
+def splitgds( gdsname, wd = '', id = 'part' ):
+   ps = lofar.parameterset.parameterset( gdsname )
+   clusterdesc = ps.getString("ClusterDesc")
+   starttime = ps.getString("StartTime")
+   endtime = ps.getString("EndTime")
+   steptime = ps.getString("StepTime")
+   N = ps.getInt("NParts")
+   gdslist = []
+   for i in range(N):
+      partname = os.path.join( wd, "%s-%i.gds" % (id, i) )
+      ps_part = ps.makeSubset( 'Part%i.' %i, 'Part0.')
+      NChan = ps_part.getString("Part0.NChan")
+      StartFreqs = ps_part.getString("Part0.StartFreqs")
+      EndFreqs = ps_part.getString("Part0.EndFreqs")
+
+      ps_part.add("Name", os.path.basename(partname))
+      ps_part.add("ClusterDesc", clusterdesc)
+      ps_part.add("StartTime", starttime)
+      ps_part.add("EndTime", endtime)
+      ps_part.add("StepTime", steptime)
+      ps_part.add("NChan", NChan)
+      ps_part.add("StartFreqs", StartFreqs)
+      ps_part.add("EndFreqs", EndFreqs)
+      ps_part.add("NParts", "1")
+      ps_part.writeFile( partname )
+      gdslist.append( partname )
+   return gdslist
+
+
+class ProgressBar:
+   
+   def __init__(self, length, message = ''):
+      self.length = length
+      self.current = 0
+      sys.stdout.write(message + '0%')
+      sys.stdout.flush()
+
+   def update(self, value):
+      while self.current < 2*int(50*value/self.length):
+         self.current += 2
+         if self.current % 10 == 0 :
+            sys.stdout.write(str(self.current) + '%')
+         else:
+            sys.stdout.write('.')
+         sys.stdout.flush()
+            
+   def finished(self):
+      self.update(self.length)
+      sys.stdout.write('\n')
+      sys.stdout.flush()
+      
diff --git a/CEP/Calibration/ExpIon/src/mpfit.py b/CEP/Calibration/ExpIon/src/mpfit.py
new file mode 100644
index 0000000000000000000000000000000000000000..2cf1ae187968bcb45706cbe24b8b7708b4026881
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/mpfit.py
@@ -0,0 +1,2593 @@
+###############################################################################
+
+# import Python modules
+
+# import 3rd party modules
+from numpy import *
+#from pylab import *
+
+# import user modules
+from .acalc import *
+
+###############################################################################
+
+"""
+20070202 HI: This documentation is not up-to-date.
+
+Perform Levenberg-Marquardt least-squares minimization, based on MINPACK-1.
+
+                                   AUTHORS
+  The original version of this software, called LMFIT, was written in FORTRAN
+  as part of the MINPACK-1 package by XXX.
+
+  Craig Markwardt converted the FORTRAN code to IDL.  The information for the
+  IDL version is:
+     Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
+     craigm@lheamail.gsfc.nasa.gov
+     UPDATED VERSIONs can be found on my WEB PAGE: 
+        http://cow.physics.wisc.edu/~craigm/idl/idl.html
+
+  Mark Rivers created this Python version from Craig's IDL version.
+    Mark Rivers, University of Chicago
+    Building 434A, Argonne National Laboratory
+    9700 South Cass Avenue, Argonne, IL 60439
+    rivers@cars.uchicago.edu
+    Updated versions can be found at http://cars.uchicago.edu/software
+
+
+                                 DESCRIPTION
+
+ MPFIT uses the Levenberg-Marquardt technique to solve the
+ least-squares problem.  In its typical use, MPFIT will be used to
+ fit a user-supplied function (the "model") to user-supplied data
+ points (the "data") by adjusting a set of parameters.  MPFIT is
+ based upon MINPACK-1 (LMDIF.F) by More' and collaborators.
+
+ For example, a researcher may think that a set of observed data
+ points is best modelled with a Gaussian curve.  A Gaussian curve is
+ parameterized by its mean, standard deviation and normalization.
+ MPFIT will, within certain constraints, find the set of parameters
+ which best fits the data.  The fit is "best" in the least-squares
+ sense; that is, the sum of the weighted squared differences between
+ the model and data is minimized.
+
+ The Levenberg-Marquardt technique is a particular strategy for
+ iteratively searching for the best fit.  This particular
+ implementation is drawn from MINPACK-1 (see NETLIB), and is much faster
+ and more accurate than the version provided in the Scientific Python package
+ in Scientific.Functions.LeastSquares.
+ This version allows upper and lower bounding constraints to be placed on each
+ parameter, or the parameter can be held fixed.
+
+ The user-supplied Python function should return an array of weighted
+ deviations between model and data.  In a typical scientific problem
+ the residuals should be weighted so that each deviate has a
+ gaussian sigma of 1.0.  If X represents values of the independent
+ variable, Y represents a measurement for each value of X, and ERR
+ represents the error in the measurements, then the deviates could
+ be calculated as follows:
+
+   DEVIATES = (Y - F(X)) / ERR
+
+ where F is the analytical function representing the model.  You are
+ recommended to use the convenience functions MPFITFUN and
+ MPFITEXPR, which are driver functions that calculate the deviates
+ for you.  If ERR are the 1-sigma uncertainties in Y, then
+
+   TOTAL( DEVIATES^2 ) 
+
+ will be the total chi-squared value.  MPFIT will minimize the
+ chi-square value.  The values of X, Y and ERR are passed through
+ MPFIT to the user-supplied function via the FUNCTKW keyword.
+
+ Simple constraints can be placed on parameter values by using the
+ PARINFO keyword to MPFIT.  See below for a description of this
+ keyword.
+
+ MPFIT does not perform more general optimization tasks.  See TNMIN
+ instead.  MPFIT is customized, based on MINPACK-1, to the
+ least-squares minimization problem.
+
+
+                               USER FUNCTION
+
+ The user must define a function which returns the appropriate
+ values as specified above.  The function should return the weighted
+ deviations between the model and the data.  It should also return a status
+ flag and an optional partial derivative array.  For applications which
+ use finite-difference derivatives -- the default -- the user
+ function should be declared in the following way:
+
+   def myfunct(p, fjac=None, x=None, y=None, err=None)
+    # Parameter values are passed in "p"
+    # If fjac==None then partial derivatives should not be
+    # computed.  It will always be None if MPFIT is called with default
+    # flag.
+    model = F(x, p)
+    # Non-negative status value means MPFIT should continue, negative means
+    # stop the calculation.
+    status = 0
+    return([status, (y-model)/err]
+
+ See below for applications with analytical derivatives.
+
+ The keyword parameters X, Y, and ERR in the example above are
+ suggestive but not required.  Any parameters can be passed to
+ MYFUNCT by using the functkw keyword to MPFIT.  Use MPFITFUN and
+ MPFITEXPR if you need ideas on how to do that.  The function *must*
+ accept a parameter list, P.
+ 
+ In general there are no restrictions on the number of dimensions in
+ X, Y or ERR.  However the deviates *must* be returned in a
+ one-dimensional Numeric array of type Float.
+
+ User functions may also indicate a fatal error condition using the
+ status return described above. If status is set to a number between
+ -15 and -1 then MPFIT will stop the calculation and return to the caller.
+
+
+                            ANALYTIC DERIVATIVES
+
+ In the search for the best-fit solution, MPFIT by default
+ calculates derivatives numerically via a finite difference
+ approximation.  The user-supplied function need not calculate the
+ derivatives explicitly.  However, if you desire to compute them
+ analytically, then the AUTODERIVATIVE=0 keyword must be passed to MPFIT.
+ As a practical matter, it is often sufficient and even faster to allow
+ MPFIT to calculate the derivatives numerically, and so
+ AUTODERIVATIVE=0 is not necessary.
+
+ If AUTODERIVATIVE=0 is used then the user function must check the parameter
+ FJAC, and if FJAC!=None then return the partial derivative array in the
+ return list.
+   def myfunct(p, fjac=None, x=None, y=None, err=None)
+    # Parameter values are passed in "p"
+    # If FJAC!=None then partial derivatives must be computed.
+    # FJAC contains an array of len(p), where each entry
+    # is 1 if that parameter is free and 0 if it is fixed. 
+    model = F(x, p)
+    Non-negative status value means MPFIT should continue, negative means
+    # stop the calculation.
+    status = 0
+    if (dojac):
+       pderiv = Numeric.zeros([len(x), len(p)], typecode=Numeric.Float)
+       for j in range(len(p)):
+         pderiv[:,j] = FGRAD(x, p, j)
+    else:
+       pderiv = None
+    return([status, (y-model)/err, pderiv]
+
+ where FGRAD(x, p, i) is a user function which must compute the
+ derivative of the model with respect to parameter P[i] at X.  When
+ finite differencing is used for computing derivatives (ie, when
+ AUTODERIVATIVE=1), or when MPFIT needs only the errors but not the
+ derivatives the parameter FJAC=None.  
+
+ Derivatives should be returned in the PDERIV array. PDERIV should be an m x
+ n array, where m is the number of data points and n is the number
+ of parameters.  dp[i,j] is the derivative at the ith point with
+ respect to the jth parameter.  
+ 
+ The derivatives with respect to fixed parameters are ignored; zero
+ is an appropriate value to insert for those derivatives.  Upon
+ input to the user function, FJAC is set to a vector with the same
+ length as P, with a value of 1 for a parameter which is free, and a
+ value of zero for a parameter which is fixed (and hence no
+ derivative needs to be calculated).
+
+ If the data is higher than one dimensional, then the *last*
+ dimension should be the parameter dimension.  Example: fitting a
+ 50x50 image, "dp" should be 50x50xNPAR.
+
+ 
+           CONSTRAINING PARAMETER VALUES WITH THE PARINFO KEYWORD
+
+ The behavior of MPFIT can be modified with respect to each
+ parameter to be fitted.  A parameter value can be fixed; simple
+ boundary constraints can be imposed; limitations on the parameter
+ changes can be imposed; properties of the automatic derivative can
+ be modified; and parameters can be tied to one another.
+
+ These properties are governed by the PARINFO structure, which is
+ passed as a keyword parameter to MPFIT.
+
+ PARINFO should be a list of dictionaries, one list entry for each parameter.
+ Each parameter is associated with one element of the array, in
+ numerical order.  The dictionary can have the following keys
+ (none are required, keys are case insensitive):
+ 
+    'value' - the starting parameter value (but see the START_PARAMS
+             parameter for more information).
+ 
+    'fixed' - a boolean value, whether the parameter is to be held
+             fixed or not.  Fixed parameters are not varied by
+             MPFIT, but are passed on to MYFUNCT for evaluation.
+ 
+    'limited' - a two-element boolean array.  If the first/second
+               element is set, then the parameter is bounded on the
+               lower/upper side.  A parameter can be bounded on both
+               sides.  Both LIMITED and LIMITS must be given
+               together.
+ 
+    'limits' - a two-element float array.  Gives the
+              parameter limits on the lower and upper sides,
+              respectively.  Zero, one or two of these values can be
+              set, depending on the values of LIMITED.  Both LIMITED
+              and LIMITS must be given together.
+ 
+    'parname' - a string, giving the name of the parameter.  The
+               fitting code of MPFIT does not use this tag in any
+               way.  However, the default iterfunct will print the
+               parameter name if available.
+ 
+    'step' - the step size to be used in calculating the numerical
+            derivatives.  If set to zero, then the step size is
+            computed automatically.  Ignored when AUTODERIVATIVE=0.
+
+    'mpside' - the sidedness of the finite difference when computing
+              numerical derivatives.  This field can take four
+              values:
+
+                 0 - one-sided derivative computed automatically
+                 1 - one-sided derivative (f(x+h) - f(x)  )/h
+               - 1 - one-sided derivative (f(x)   - f(x-h))/h
+                 2 - two-sided derivative (f(x+h) - f(x-h))/(2*h)
+
+             Where H is the STEP parameter described above.  The
+             "automatic" one-sided derivative method will chose a
+             direction for the finite difference which does not
+             violate any constraints.  The other methods do not
+             perform this check.  The two-sided method is in
+             principle more precise, but requires twice as many
+             function evaluations.  Default: 0.
+
+    'mpmaxstep' - the maximum change to be made in the parameter
+                 value.  During the fitting process, the parameter
+                 will never be changed by more than this value in
+                 one iteration.
+
+                 A value of 0 indicates no maximum.  Default: 0.
+ 
+    'tied' - a string expression which "ties" the parameter to other
+            free or fixed parameters.  Any expression involving
+            constants and the parameter array P are permitted.
+            Example: if parameter 2 is always to be twice parameter
+            1 then use the following: parinfo(2).tied = '2 * p(1)'.
+            Since they are totally constrained, tied parameters are
+            considered to be fixed; no errors are computed for them.
+            [ NOTE: the PARNAME can't be used in expressions. ]
+
+    'mpprint' - if set to 1, then the default iterfunct will print the
+               parameter value.  If set to 0, the parameter value
+               will not be printed.  This tag can be used to
+               selectively print only a few parameter values out of
+               many.  Default: 1 (all parameters printed)
+
+ 
+ Future modifications to the PARINFO structure, if any, will involve
+ adding dictionary tags beginning with the two letters "MP".
+ Therefore programmers are urged to avoid using tags starting with
+ the same letters; otherwise they are free to include their own
+ fields within the PARINFO structure, and they will be ignored.
+ 
+ PARINFO Example:
+ parinfo = [{'value':0., 'fixed':0, 'limited':[0,0], 'limits':[0.,0.]}]*5
+ parinfo[0]['fixed'] = 1
+ parinfo[4]['limited'][0] = 1
+ parinfo[4]['limits'][0]  = 50.
+ values = [5.7, 2.2, 500., 1.5, 2000.]
+ for i in range(5): parinfo[i]['value']=values[i]
+ 
+ A total of 5 parameters, with starting values of 5.7,
+ 2.2, 500, 1.5, and 2000 are given.  The first parameter
+ is fixed at a value of 5.7, and the last parameter is
+ constrained to be above 50.
+
+
+                                   EXAMPLE
+
+   import mpfit
+   import Numeric
+   x = Numeric.arange(100, Numeric.float)
+   p0 = [5.7, 2.2, 500., 1.5, 2000.]
+   y = ( p[0] + p[1]*[x] + p[2]*[x**2] + p[3]*Numeric.sqrt(x) +
+         p[4]*Numeric.log(x))
+   fa = {'x':x, 'y':y, 'err':err}
+   m = mpfit('myfunct', p0, functkw=fa)
+   print 'status = ', m.status
+   if (m.status <= 0): print 'error message = ', m.errmsg
+   print 'parameters = ', m.params
+
+   Minimizes sum of squares of MYFUNCT.  MYFUNCT is called with the X,
+   Y, and ERR keyword parameters that are given by FUNCTKW.  The
+   results can be obtained from the returned object m.
+
+
+                            THEORY OF OPERATION
+
+   There are many specific strategies for function minimization.  One
+   very popular technique is to use function gradient information to
+   realize the local structure of the function.  Near a local minimum
+   the function value can be taylor expanded about x0 as follows:
+
+      f(x) = f(x0) + f'(x0) . (x-x0) + (1/2) (x-x0) . f''(x0) . (x-x0)
+             -----   ---------------   -------------------------------  (1)
+     Order    0th          1st                      2nd
+
+   Here f'(x) is the gradient vector of f at x, and f''(x) is the
+   Hessian matrix of second derivatives of f at x.  The vector x is
+   the set of function parameters, not the measured data vector.  One
+   can find the minimum of f, f(xm) using Newton's method, and
+   arrives at the following linear equation:
+
+      f''(x0) . (xm-x0) = - f'(x0)                            (2)
+
+   If an inverse can be found for f''(x0) then one can solve for
+   (xm-x0), the step vector from the current position x0 to the new
+   projected minimum.  Here the problem has been linearized (ie, the
+   gradient information is known to first order).  f''(x0) is
+   symmetric n x n matrix, and should be positive definite.
+
+   The Levenberg - Marquardt technique is a variation on this theme.
+   It adds an additional diagonal term to the equation which may aid the
+   convergence properties:
+
+      (f''(x0) + nu I) . (xm-x0) = -f'(x0)                  (2a)
+
+   where I is the identity matrix.  When nu is large, the overall
+   matrix is diagonally dominant, and the iterations follow steepest
+   descent.  When nu is small, the iterations are quadratically
+   convergent.
+
+   In principle, if f''(x0) and f'(x0) are known then xm-x0 can be
+   determined.  However the Hessian matrix is often difficult or
+   impossible to compute.  The gradient f'(x0) may be easier to
+   compute, if even by finite difference techniques.  So-called
+   quasi-Newton techniques attempt to successively estimate f''(x0)
+   by building up gradient information as the iterations proceed.
+
+   In the least squares problem there are further simplifications
+   which assist in solving eqn (2).  The function to be minimized is
+   a sum of squares:
+
+       f = Sum(hi^2)                                         (3)
+
+   where hi is the ith residual out of m residuals as described
+   above.  This can be substituted back into eqn (2) after computing
+   the derivatives:
+
+       f'  = 2 Sum(hi  hi')     
+       f'' = 2 Sum(hi' hj') + 2 Sum(hi hi'')                (4)
+
+   If one assumes that the parameters are already close enough to a
+   minimum, then one typically finds that the second term in f'' is
+   negligible [or, in any case, is too difficult to compute].  Thus,
+   equation (2) can be solved, at least approximately, using only
+   gradient information.
+
+   In matrix notation, the combination of eqns (2) and (4) becomes:
+
+        hT' . h' . dx = - hT' . h                          (5)
+
+   Where h is the residual vector (length m), hT is its transpose, h'
+   is the Jacobian matrix (dimensions n x m), and dx is (xm-x0).  The
+   user function supplies the residual vector h, and in some cases h'
+   when it is not found by finite differences (see MPFIT_FDJAC2,
+   which finds h and hT').  Even if dx is not the best absolute step
+   to take, it does provide a good estimate of the best *direction*,
+   so often a line minimization will occur along the dx vector
+   direction.
+
+   The method of solution employed by MINPACK is to form the Q . R
+   factorization of h', where Q is an orthogonal matrix such that QT .
+   Q = I, and R is upper right triangular.  Using h' = Q . R and the
+   ortogonality of Q, eqn (5) becomes
+
+        (RT . QT) . (Q . R) . dx = - (RT . QT) . h
+                     RT . R . dx = - RT . QT . h         (6)
+                          R . dx = - QT . h
+
+   where the last statement follows because R is upper triangular.
+   Here, R, QT and h are known so this is a matter of solving for dx.
+   The routine MPFIT_QRFAC provides the QR factorization of h, with
+   pivoting, and MPFIT_QRSOLV provides the solution for dx.
+
+   
+                                 REFERENCES
+
+   MINPACK-1, Jorge More', available from netlib (www.netlib.org).
+   "Optimization Software Guide," Jorge More' and Stephen Wright, 
+     SIAM, *Frontiers in Applied Mathematics*, Number 14.
+   More', Jorge J., "The Levenberg-Marquardt Algorithm:
+     Implementation and Theory," in *Numerical Analysis*, ed. Watson,
+     G. A., Lecture Notes in Mathematics 630, Springer-Verlag, 1977.
+
+
+                           MODIFICATION HISTORY
+
+   Translated from MINPACK-1 in FORTRAN, Apr-Jul 1998, CM
+ Copyright (C) 1997-2002, Craig Markwardt
+ This software is provided as is without any warranty whatsoever.
+ Permission to use, copy, modify, and distribute modified or
+ unmodified copies is granted, provided this copyright and disclaimer
+ are included unchanged.
+
+   Translated from MPFIT (Craig Markwardt's IDL package) to Python,
+   August, 2002.  Mark Rivers
+"""
+
+###############################################################################
+
+#     Original FORTRAN documentation
+#     **********
+#
+#     subroutine lmdif
+#
+#     the purpose of lmdif is to minimize the sum of the squares of
+#     m nonlinear functions in n variables by a modification of
+#     the levenberg-marquardt algorithm. the user must provide a
+#     subroutine which calculates the functions. the jacobian is
+#     then calculated by a forward-difference approximation.
+#
+#     the subroutine statement is
+#
+#       subroutine lmdif(fcn,m,n,x,fvec,ftol,xtol,gtol,maxfev,epsfcn,
+#                        diag,mode,factor,nprint,info,nfev,fjac,
+#                        ldfjac,ipvt,qtf,wa1,wa2,wa3,wa4)
+#
+#     where
+#
+#       fcn is the name of the user-supplied subroutine which
+#         calculates the functions. fcn must be declared
+#         in an external statement in the user calling
+#         program, and should be written as follows.
+#
+#         subroutine fcn(m,n,x,fvec,iflag)
+#         integer m,n,iflag
+#         double precision x(n),fvec(m)
+#         ----------
+#         calculate the functions at x and
+#         return this vector in fvec.
+#         ----------
+#         return
+#         end
+#
+#         the value of iflag should not be changed by fcn unless
+#         the user wants to terminate execution of lmdif.
+#         in this case set iflag to a negative integer.
+#
+#       m is a positive integer input variable set to the number
+#         of functions.
+#
+#       n is a positive integer input variable set to the number
+#         of variables. n must not exceed m.
+#
+#       x is an array of length n. on input x must contain
+#         an initial estimate of the solution vector. on output x
+#         contains the final estimate of the solution vector.
+#
+#       fvec is an output array of length m which contains
+#         the functions evaluated at the output x.
+#
+#       ftol is a nonnegative input variable. termination
+#         occurs when both the actual and predicted relative
+#         reductions in the sum of squares are at most ftol.
+#         therefore, ftol measures the relative error desired
+#         in the sum of squares.
+#
+#       xtol is a nonnegative input variable. termination
+#         occurs when the relative error between two consecutive
+#         iterates is at most xtol. therefore, xtol measures the
+#         relative error desired in the approximate solution.
+#
+#       gtol is a nonnegative input variable. termination
+#         occurs when the cosine of the angle between fvec and
+#         any column of the jacobian is at most gtol in absolute
+#         value. therefore, gtol measures the orthogonality
+#         desired between the function vector and the columns
+#         of the jacobian.
+#
+#       maxfev is a positive integer input variable. termination
+#         occurs when the number of calls to fcn is at least
+#         maxfev by the end of an iteration.
+#
+#       epsfcn is an input variable used in determining a suitable
+#         step length for the forward-difference approximation. this
+#         approximation assumes that the relative errors in the
+#         functions are of the order of epsfcn. if epsfcn is less
+#         than the machine precision, it is assumed that the relative
+#         errors in the functions are of the order of the machine
+#         precision.
+#
+#       diag is an array of length n. if mode = 1 (see
+#         below), diag is internally set. if mode = 2, diag
+#         must contain positive entries that serve as
+#         multiplicative scale factors for the variables.
+#
+#       mode is an integer input variable. if mode = 1, the
+#         variables will be scaled internally. if mode = 2,
+#         the scaling is specified by the input diag. other
+#         values of mode are equivalent to mode = 1.
+#
+#       factor is a positive input variable used in determining the
+#         initial step bound. this bound is set to the product of
+#         factor and the euclidean norm of diag*x if nonzero, or else
+#         to factor itself. in most cases factor should lie in the
+#         interval (.1,100.). 100. is a generally recommended value.
+#
+#       nprint is an integer input variable that enables controlled
+#         printing of iterates if it is positive. in this case,
+#         fcn is called with iflag = 0 at the beginning of the first
+#         iteration and every nprint iterations thereafter and
+#         immediately prior to return, with x and fvec available
+#         for printing. if nprint is not positive, no special calls
+#         of fcn with iflag = 0 are made.
+#
+#       info is an integer output variable. if the user has
+#         terminated execution, info is set to the (negative)
+#         value of iflag. see description of fcn. otherwise,
+#         info is set as follows.
+#
+#         info = 0  improper input parameters.
+#
+#         info = 1  both actual and predicted relative reductions
+#                   in the sum of squares are at most ftol.
+#
+#         info = 2  relative error between two consecutive iterates
+#                   is at most xtol.
+#
+#         info = 3  conditions for info = 1 and info = 2 both hold.
+#
+#         info = 4  the cosine of the angle between fvec and any
+#                   column of the jacobian is at most gtol in
+#                   absolute value.
+#
+#         info = 5  number of calls to fcn has reached or
+#                   exceeded maxfev.
+#
+#         info = 6  ftol is too small. no further reduction in
+#                   the sum of squares is possible.
+#
+#         info = 7  xtol is too small. no further improvement in
+#                   the approximate solution x is possible.
+#
+#         info = 8  gtol is too small. fvec is orthogonal to the
+#                   columns of the jacobian to machine precision.
+#
+#       nfev is an integer output variable set to the number of
+#         calls to fcn.
+#
+#       fjac is an output m by n array. the upper n by n submatrix
+#         of fjac contains an upper triangular matrix r with
+#         diagonal elements of nonincreasing magnitude such that
+#
+#                t     t           t
+#               p *(jac *jac)*p = r *r,
+#
+#         where p is a permutation matrix and jac is the final
+#         calculated jacobian. column j of p is column ipvt(j)
+#         (see below) of the identity matrix. the lower trapezoidal
+#         part of fjac contains information generated during
+#         the computation of r.
+#
+#       ldfjac is a positive integer input variable not less than m
+#         which specifies the leading dimension of the array fjac.
+#
+#       ipvt is an integer output array of length n. ipvt
+#         defines a permutation matrix p such that jac*p = q*r,
+#         where jac is the final calculated jacobian, q is
+#         orthogonal (not stored), and r is upper triangular
+#         with diagonal elements of nonincreasing magnitude.
+#         column j of p is column ipvt(j) of the identity matrix.
+#
+#       qtf is an output array of length n which contains
+#         the first n elements of the vector (q transpose)*fvec.
+#
+#       wa1, wa2, and wa3 are work arrays of length n.
+#
+#       wa4 is a work array of length m.
+#
+#     subprograms called
+#
+#       user-supplied ...... fcn
+#
+#       minpack-supplied ... dpmpar,enorm,fdjac2,,qrfac
+#
+#       fortran-supplied ... dabs,dmax1,dmin1,dsqrt,mod
+#
+#     argonne national laboratory. minpack project. march 1980.
+#     burton s. garbow, kenneth e. hillstrom, jorge j. more
+#
+#     **********
+
+###############################################################################
+
+class mpfit:
+
+   def __init__( self, fcn, xall = None, functkw = {}, parinfo = None,
+                ftol = 1.e-10, xtol = 1.e-10, gtol = 1.e-10, double = True,
+                damp = 0., maxiter = 200, factor = 100., nprint = 1,
+                iterfunct = 'default', iterkw = {}, nocovar = False,
+                fastnorm = False, rescale = False, autoderivative = True,
+                quiet = False, diag = None, epsfcn = None, debug = False ):
+
+      """
+Inputs:
+  fcn:
+     The function to be minimized.  The function should return the weighted
+     deviations between the model and the data, as described above.
+
+  xall:
+     An array of starting values for each of the parameters of the model.
+     The number of parameters should be fewer than the number of measurements.
+
+     This parameter is optional if the parinfo keyword is used (but see
+     parinfo).  The parinfo keyword provides a mechanism to fix or constrain
+     individual parameters.  
+
+Keywords:
+
+   autoderivative:
+      If this is set, derivatives of the function will be computed
+      automatically via a finite differencing procedure.  If not set, then
+      fcn must provide the (analytical) derivatives.
+         Default: set (=1) 
+         NOTE: to supply your own analytical derivatives,
+               explicitly pass autoderivative=0
+
+   fastnorm:
+      Set this keyword to select a faster algorithm to compute sum-of-square
+      values internally.  For systems with large numbers of data points, the
+      standard algorithm can become prohibitively slow because it cannot be
+      vectorized well.  By setting this keyword, MPFIT will run faster, but
+      it will be more prone to floating point overflows and underflows.  Thus, setting
+      this keyword may sacrifice some stability in the fitting process.
+         Default: clear (=0)
+              
+   ftol:
+      A nonnegative input variable. Termination occurs when both the actual
+      and predicted relative reductions in the sum of squares are at most
+      ftol (and status is accordingly set to 1 or 3).  Therefore, ftol
+      measures the relative error desired in the sum of squares.
+         Default: 1E-10
+
+   functkw:
+      A dictionary which contains the parameters to be passed to the
+      user-supplied function specified by fcn via the standard Python
+      keyword dictionary mechanism.  This is the way you can pass additional
+      data to your user-supplied function without using global variables.
+
+      Consider the following example:
+         if functkw = {'xval':[1.,2.,3.], 'yval':[1.,4.,9.],
+                       'errval':[1.,1.,1.] }
+      then the user supplied function should be declared like this:
+         def myfunct(p, fjac=None, xval=None, yval=None, errval=None):
+
+      Default: {}   No extra parameters are passed to the user-supplied
+                    function. 
+
+   gtol:
+      A nonnegative input variable. Termination occurs when the cosine of
+      the angle between fvec and any column of the jacobian is at most gtol
+      in absolute value (and status is accordingly set to 4). Therefore,
+      gtol measures the orthogonality desired between the function vector
+      and the columns of the jacobian.
+         Default: 1e-10
+
+   iterkw:
+      The keyword arguments to be passed to iterfunct via the dictionary
+      keyword mechanism.  This should be a dictionary and is similar in
+      operation to FUNCTKW.
+         Default: {}  No arguments are passed.
+
+   iterfunct:
+      The name of a function to be called upon each NPRINT iteration of the
+      MPFIT routine.  It should be declared in the following way:
+         def iterfunct(myfunct, p, iter, fnorm, functkw=None, 
+                       parinfo=None, quiet=0, dof=None, [iterkw keywords here])
+         # perform custom iteration update
+         
+      iterfunct must accept all three keyword parameters (FUNCTKW, PARINFO
+      and QUIET). 
+          
+      myfunct:  The user-supplied function to be minimized,
+      p:        The current set of model parameters
+      iter:     The iteration number
+      functkw:  The arguments to be passed to myfunct.
+      fnorm:    The chi-squared value.
+      quiet:    Set when no textual output should be printed.
+      dof:      The number of degrees of freedom, normally the number of points
+                less the number of free parameters.
+      See below for documentation of parinfo.
+
+      In implementation, iterfunct can perform updates to the terminal or
+      graphical user interface, to provide feedback while the fit proceeds.
+      If the fit is to be stopped for any reason, then iterfunct should return a
+      a status value between - 15 and - 1.  Otherwise it should return 0.
+      In principle, iterfunct should probably not modify the parameter values,
+      because it may interfere with the algorithm's stability.  In practice it
+      is allowed.
+
+      Default: an internal routine is used to print the parameter values.
+
+      Set iterfunct=None if there is no user-defined routine and you don't
+      want the internal default routine be called.
+
+   maxiter:
+      The maximum number of iterations to perform.  If the number is exceeded,
+      then the status value is set to 5 and MPFIT returns.
+      Default: 200 iterations
+
+   nocovar:
+      Set this keyword to prevent the calculation of the covariance matrix
+      before returning (see COVAR)
+      Default: clear (=0)  The covariance matrix is returned
+
+   nprint:
+      The frequency with which iterfunct is called.  A value of 1 indicates
+      that iterfunct is called with every iteration, while 2 indicates every
+      other iteration, etc.  Note that several Levenberg-Marquardt attempts
+      can be made in a single iteration.
+      Default value: 1
+
+   parinfo
+      Provides a mechanism for more sophisticated constraints to be placed on
+      parameter values.  When parinfo is not passed, then it is assumed that
+      all parameters are free and unconstrained.  Values in parinfo are never
+      modified during a call to MPFIT.
+
+      See description above for the structure of PARINFO.
+
+      Default value: None  All parameters are free and unconstrained.
+
+   quiet:
+      Set this keyword when no textual output should be printed by MPFIT
+
+   damp:
+      A scalar number, indicating the cut-off value of residuals where
+      "damping" will occur.  Residuals with magnitudes greater than this
+      number will be replaced by their hyperbolic tangent.  This partially
+      mitigates the so-called large residual problem inherent in
+      least-squares solvers (as for the test problem CURVI,
+      http://www.maxthis.com/curviex.htm).
+      A value of 0 indicates no damping.
+         Default: 0
+
+      Note: DAMP doesn't work with autoderivative=0
+
+   xtol:
+      A nonnegative input variable. Termination occurs when the relative error
+      between two consecutive iterates is at most xtol (and status is
+      accordingly set to 2 or 3).  Therefore, xtol measures the relative error
+      desired in the approximate solution.
+      Default: 1E-10
+
+ Outputs:
+
+   Returns an object of type mpfit.  The results are attributes of this class,
+   e.g. mpfit.status, mpfit.errmsg, mpfit.params, npfit.niter, mpfit.covar.
+
+   .status
+      An integer status code is returned.  All values greater than zero can
+      represent success (however .status == 5 may indicate failure to
+      converge). It can have one of the following values:
+
+      - 16
+         A parameter or function value has become infinite or an undefined
+         number.  This is usually a consequence of numerical overflow in the
+         user's model function, which must be avoided.
+
+      - 15 to - 1 
+         These are error codes that either MYFUNCT or iterfunct may return to
+         terminate the fitting process.  Values from - 15 to - 1 are reserved
+         for the user functions and will not clash with MPFIT.
+
+      0  Improper input parameters.
+         
+      1  Both actual and predicted relative reductions in the sum of squares
+         are at most ftol.
+         
+      2  Relative error between two consecutive iterates is at most xtol
+         
+      3  Conditions for status = 1 and status = 2 both hold.
+         
+      4  The cosine of the angle between fvec and any column of the jacobian
+         is at most gtol in absolute value.
+         
+      5  The maximum number of iterations has been reached.
+         
+      6  ftol is too small. No further reduction in the sum of squares is
+         possible.
+         
+      7  xtol is too small. No further improvement in the approximate solution
+         x is possible.
+         
+      8  gtol is too small. fvec is orthogonal to the columns of the jacobian
+         to machine precision.
+
+   .fnorm
+      The value of the summed squared residuals for the returned parameter
+      values.
+
+   .covar
+      The covariance matrix for the set of parameters returned by MPFIT.
+      The matrix is NxN where N is the number of  parameters.  The square root
+      of the diagonal elements gives the formal 1-sigma statistical errors on
+      the parameters if errors were treated "properly" in fcn.
+      Parameter errors are also returned in .perror.
+
+      To compute the correlation matrix, pcor, use this example:
+         cov = mpfit.covar
+         pcor = cov * 0.
+         for i in range(n):
+            for j in range(n):
+               pcor[i,j] = cov[i,j]/Numeric.sqrt(cov[i,i]*cov[j,j])
+
+      If nocovar is set or MPFIT terminated abnormally, then .covar is set to
+      a scalar with value None.
+
+   .errmsg
+      A string error or warning message is returned.
+
+   .nfev
+      The number of calls to MYFUNCT performed.
+
+   .niter
+      The number of iterations completed.
+
+   .perror
+      The formal 1-sigma errors in each parameter, computed from the
+      covariance matrix.  If a parameter is held fixed, or if it touches a
+      boundary, then the error is reported as zero.
+
+      If the fit is unweighted (i.e. no errors were given, or the weights
+      were uniformly set to unity), then .perror will probably not represent
+      the true parameter uncertainties.  
+
+      *If* you can assume that the true reduced chi-squared value is unity --
+      meaning that the fit is implicitly assumed to be of good quality --
+      then the estimated parameter uncertainties can be computed by scaling
+      .perror by the measured chi-squared value.
+
+         dof = len(x) - len(mpfit.params) # deg of freedom
+         # scaled uncertainties
+         pcerror = mpfit.perror * Numeric.sqrt(mpfit.fnorm / dof)
+
+      """
+
+      self.niter = 0
+      self.params = None
+      self.covar = None
+      self.perror = None
+      self.status = 0  # Invalid input flag set while we check inputs
+      self.debug = debug
+      self.errmsg = ''
+      self.fastnorm = fastnorm
+      self.nfev = 0
+      self.damp = damp
+      self.machar = machar( double = double )
+      machep = self.machar.machep
+      self.diag = diag
+      if double:
+        self.dtype = float64
+      else:
+        self.dtype = float32
+      try:
+        import _mpfit
+      except:
+        self.__mpfit = False
+      else:
+        self.__mpfit = True
+
+      if ( fcn == None ):
+         self.errmsg = "Usage: parms = mpfit('myfunt', ... )"
+         return
+
+      if ( iterfunct == 'default' ):
+         iterfunct = self.defiter
+
+      ## Parameter damping doesn't work when user is providing their own
+      ## gradients.
+      if ( ( self.damp != 0. ) and ( autoderivative == False ) ):
+         self.errmsg =  'ERROR: keywords DAMP and AUTODERIVATIVE are mutually exclusive'
+         return
+
+# 20070130 HI: TODO - iterstop and iterkeystop
+
+      ## Parameters can either be stored in parinfo, or x. x takes precedence if it exists
+      if ( ( xall == None ) and ( parinfo == None ) ):
+         self.errmsg = 'ERROR: must pass parameters in P or PARINFO'
+         return
+
+      ## Be sure that PARINFO is of the right type
+      if ( parinfo != None ):
+         if ( type( parinfo ) != type( [] ) ):
+            self.errmsg = 'ERROR: PARINFO must be a list of dictionaries.'
+            return
+         if ( len( parinfo ) == 0 ):
+            self.errmsg = 'ERROR: PARINFO must be a non-empty list of dictionaries.'
+            return
+         if ( type( parinfo[ 0 ] ) != type( {} ) ):
+            self.errmsg = 'ERROR: PARINFO must be a list of dictionaries.'
+            return
+         if ( ( xall != None ) and ( len( xall ) != len( parinfo ) ) ):
+            self.errmsg = 'ERROR: number of elements in PARINFO and P must agree'
+            return
+
+      ## If the parameters were not specified at the command line, then
+      ## extract them from PARINFO
+      if ( xall == None ):
+         xall = self.parinfo( parinfo = parinfo, key = 'value' )
+         if ( xall == None ):
+            self.errmsg = 'ERROR: either P or PARINFO(*)["value"] must be supplied.'
+            return
+
+      ## Make sure parameters are of the specified internal precision
+      xall = array( xall, dtype = self.dtype )
+
+      npar = len( xall )
+      self.fnorm = - 1.
+      fnorm1 = - 1.
+
+      ## TIED parameters?
+      ptied = self.parinfo( parinfo = parinfo, key = 'tied', default = '', n = npar )
+      self.ptied = [ ptied[ i ].strip() for i in range( npar ) ]
+      ptied = array( [ ( self.ptied[ i ] != '' ) for i in range( npar ) ], dtype = bool )
+      self.qanytied = sometrue( ptied )
+
+      ## FIXED parameters ?
+#      pfixed = self.parinfo( parinfo = parinfo, key = 'fixed', default = False, n = npar )
+#      pfixed = ( pfixed == True )
+#      pfixed = ( pfixed | ptied ) ## Tied parameters are also effectively fixed
+      pfixed = ptied.copy()
+  
+      ## Finite differencing step, absolute and relative, and sidedness of deriv.
+      step = self.parinfo( parinfo = parinfo, key = 'step', default = 0., n = npar )
+      dstep = self.parinfo( parinfo = parinfo, key = 'relstep', default = 0., n = npar )
+      dside = self.parinfo( parinfo = parinfo, key = 'mpside',  default = 0, n = npar )
+
+      ## Maximum and minimum steps allowed to be taken in one iteration
+      maxstep = self.parinfo( parinfo = parinfo, key = 'mpmaxstep', default = 0., n = npar )
+      minstep = self.parinfo( parinfo = parinfo, key = 'mpminstep', default = 0., n = npar )
+      qmin = zeros( shape = minstep.shape, dtype = bool )  ## Remove minstep for now!!
+      qmax = ( maxstep != 0. )
+      if sometrue( qmin & qmax & ( maxstep < minstep ) ):
+         self.errmsg = 'ERROR: MPMINSTEP is greater than MPMAXSTEP'
+         return
+      qminmax = sometrue( qmin | qmax )
+
+# 20070130 HI: TODO - isext
+
+      ## LIMITED parameters ?
+      limits = self.parinfo( parinfo = parinfo, key = 'limits', default = [ None, None ], n = npar )
+      if ( limits != None ):
+
+         ## Error checking on limits in parinfo
+         for i in range( npar ):
+            limit = limits[ i ] # do not remove
+            if ( limit[ 0 ] != None ):
+               if ( xall[ i ] < limit[ 0 ] ):
+                  self.errmsg = 'ERROR: parameters are not within PARINFO limits'
+                  return
+            if ( limit[ 1 ] != None ):
+               if ( xall[ i ] > limit[ 1 ] ):
+                  self.errmsg = 'ERROR: parameters are not within PARINFO limits'
+                  return
+            if ( limit[ 0 ] != None ) and ( limit[ 1 ] != None ) and ( pfixed[ i ] == False ):
+               if ( limit[ 0 ] > limit[ 1 ] ):
+                  self.errmsg = 'ERROR: parameters are not within PARINFO limits'
+                  return
+               if ( limit[ 0 ] == limit[ 1 ] ):
+                  pfixed[ i ] = True
+
+      ## Update the free parameters
+      ifree = awhere( pfixed == False )
+      nfree = len( ifree )
+      if ( nfree == 0 ):
+         self.errmsg = 'ERROR: no free parameters'
+         return
+
+      ## Compose only VARYING parameters
+      self.params = xall      ## self.params is the set of parameters to be returned
+      x = aget( self.params, ifree )  ## x is the set of free parameters
+
+      if ( limits != None ):
+
+         ## Transfer structure values to local variables
+         qulim = zeros( shape = x.shape, dtype = bool )
+         ulim = zeros( shape = x.shape, dtype = self.dtype )
+         qllim = zeros( shape = x.shape, dtype = bool )
+         llim = zeros( shape = x.shape, dtype = self.dtype )
+         for i in range( nfree ):
+            limit = limits[ ifree[ i, 0 ] ] # do not remove
+            if ( limit[ 1 ] != None ):
+                 qulim[ i ] = True
+                 ulim[ i ] = limit[ 1 ]
+            if ( limit[ 0 ] != None ):
+                 qllim[ i ] = True
+                 llim[ i ] = limit[ 0 ]
+         qanylim = sometrue( qulim | qllim )
+
+      else:
+
+         ## Fill in local variables with dummy values
+         qulim = zeros( shape = x.shape, dtype = bool )
+         ulim = zeros( shape = x.shape, dtype = self.dtype )
+         qllim = zeros( shape = x.shape, dtype = bool )
+         llim = zeros( shape = x.shape, dtype = self.dtype )
+         qanylim = False
+
+      ## CYCLIC parameters ?
+      pcyclic = self.parinfo( parinfo = parinfo, key = 'cyclic', default = False, n = npar )
+      qcyclic = aget( pcyclic, ifree )
+      if sometrue( qcyclic & ( ( qulim == False ) | ( qllim == False ) ) ):
+         self.errmsg = 'ERROR: PARINFO cyclic parameter needs both upper and lower limits set'
+         return
+
+      wh = awhere( qcyclic )
+      ncyclic = len( wh )
+      qanycyclic = ( ncyclic > 0 )
+
+      ## Initialize the number of parameters pegged at a hard limit value
+      wh = awhere( ( ( qulim & ( x == ulim ) ) | ( qllim & ( x == llim ) ) ) &
+          ( qcyclic == False ) )
+      npegged = len( wh )
+
+      n = len( x )
+
+      ## Check input parameters for errors
+      if ( ( n < 0 ) or ( ftol <= 0. ) or ( xtol <= 0. ) or ( gtol <= 0. )
+                  or ( maxiter <= 0 ) or ( factor <= 0. ) ):
+         self.errmsg = 'ERROR: input keywords are inconsistent'
+         return
+ 
+      if rescale:
+         self.errmsg = 'ERROR: DIAG parameter scales are inconsistent'
+         if ( diag == None ):
+            return
+         if ( len( diag ) < n ):
+            return
+         self.diag = array( diag, dtype = self.dtype )
+         if sometrue( self.diag <= 0. ):
+            return
+         self.errmsg = ''
+
+      # Make sure x is of the specified internal type
+      x = array( x, dtype = self.dtype )
+
+# 20070130 HI: TODO - external iteration
+
+      self.status, fvec, dummy = self.call( fcn, self.params, functkw )
+      if ( self.status < 0 ):
+         self.errmsg = 'ERROR: first call to "' + str( fcn ) + '" failed'
+         return
+
+# 20070130 HI: TODO - data type check
+
+      m = len( fvec )
+      if ( m < n ):
+         self.errmsg = 'ERROR: number of parameters must not exceed data'
+         return
+
+      self.fnorm = self.enorm( fvec )
+
+      ## Initialize Levelberg-Marquardt parameter and iteration counter
+
+      par = 0.
+      self.niter = 1
+      qtf = zeros( shape = x.shape, dtype = self.dtype )
+      self.status = 0
+
+
+      ## Beginning of the outer loop
+  
+      while True:
+
+         ## If requested, call fcn to enable printing of iterates
+         self.params = aput( self.params, ifree, x )
+         if ( self.qanytied ):
+            self.params = self.tie( self.params, ptied = ptied )
+         dof = max( [ len( fvec ) - nfree, 1 ] )
+
+         if ( nprint > 0 ) and ( iterfunct != None ):
+            if ( amodulo( ( self.niter - 1 ), nprint ) == 0 ):
+               xnew0 = self.params.copy()
+
+               status = iterfunct( fcn, self.params, self.niter, self.fnorm**2, 
+                  functkw = functkw, parinfo = parinfo, quiet = quiet, 
+                  dof = dof, **iterkw)
+               if ( status != None ):
+                  self.status = status
+
+               ## Check for user termination
+               if ( self.status < 0 ):  
+                  self.errmsg = 'WARNING: premature termination by ' + str( iterfunct )
+                  return
+
+               ## If parameters were changed (grrr..) then re-tie
+               if ( ( abs( self.params - xnew0 ) ).sum() > 0. ):
+                  if self.qanytied:
+                     self.params = self.tie( self.params, ptied = ptied )
+                  x = aget( self.params, ifree )
+
+
+         ## Calculate the jacobian matrix
+         self.status = 2
+         catch_msg = 'calling MPFIT_FDJAC2'
+         fjac = self.fdjac2( fcn, x, fvec, step = step, ulimited = qulim,
+                       ulimit = ulim, dside = dside, epsfcn = epsfcn,
+                       autoderiv = autoderivative, functkw = functkw,
+                       xall = self.params, ifree = ifree, dstep = dstep )
+         if ( fjac == None ):
+            self.errmsg = 'WARNING: premature termination by FDJAC2'
+            return
+
+# 20070130 HI: TODO - put in external option
+
+         ## Determine if any of the parameters are pegged at the limits
+         npegged = 0
+         if qanylim:
+            catch_msg = 'zeroing derivatives of pegged parameters'
+            whlpeg = awhere( qllim & ( x == llim ) & ( qcyclic == False ) )
+            nlpeg = len( whlpeg )
+            whupeg = awhere( qulim & ( x == ulim ) & ( qcyclic == False ) )
+            nupeg = len( whupeg )
+            npegged = nlpeg + nupeg
+
+            ## See if any "pegged" values should keep their derivatives
+            if ( nlpeg > 0 ):
+               ## Total derivative of sum wrt lower pegged parameters
+               for i in range( nlpeg ):
+                  sum = ( fvec * fjac[ : , whlpeg [ i, 0 ] ] ).sum()
+                  if ( sum > 0 ):
+                     fjac[ : , whlpeg [ i, 0 ] ] = 0
+            if ( nupeg > 0 ):
+               ## Total derivative of sum wrt upper pegged parameters
+               for i in range( nupeg ):
+                  sum = ( fvec * fjac[ : , whupeg[ i, 0 ] ] ).sum()
+                  if ( sum < 0 ):
+                     fjac[ : , whupeg[ i, 0 ] ] = 0
+
+         ## Compute the QR factorization of the jacobian
+         fjac, ipvt, wa1, wa2 = self.qrfac( fjac, pivot = True )
+
+         ## On the first iteration if "diag" is unspecified, scale
+         ## according to the norms of the columns of the initial jacobian
+         catch_msg = 'rescaling diagonal elements'
+         if ( self.niter == 1 ):
+
+            if ( not rescale ) or ( len( self.diag ) < n ):
+               self.diag = wa2.copy()
+               wh = awhere( self.diag == 0. )
+               self.diag = aput( self.diag, wh, 1. )
+
+            ## On the first iteration, calculate the norm of the scaled x
+            ## and initialize the step bound delta 
+            wa3 = self.diag * x
+            xnorm = self.enorm( wa3 )
+            delta = factor * xnorm
+            if ( delta == 0. ):
+               delta = factor
+
+         ## Form (q transpose)*fvec and store the first n components in qtf
+         catch_msg = 'forming (q transpose)*fvec'
+         wa4 = fvec.copy()
+#         mpvt = ( transpose( repeat( [ ipvt ], n ) ) == repeat( [ range( n ) ], n ) )
+#         fjac * mpvt != 0.
+         for j in range( n ):
+            lj = ipvt[ j, 0 ]
+            temp3 = fjac[ j, lj ]
+            if ( temp3 != 0. ):
+               fj = fjac[ j : , lj ]
+               wj = wa4[ j : ]
+               ## *** optimization wa4(j:*)
+               wa4[ j : ] = wj - fj * ( fj * wj ).sum() / temp3
+            fjac[ j, lj ] = wa1[ j ]
+            qtf[ j ] = wa4[ j ]
+         ## From this point on, only the square matrix, consisting of the
+         ## triangle of R, is needed.
+         fjac = fjac[ 0 : n, 0 : n ]
+         fjac = reshape( fjac, ( n, n ) )
+         temp = fjac.copy()
+         for i in range( n ):
+            temp[ : , i ] = fjac[ : , ipvt[ i, 0 ] ]
+         fjac = temp.copy()
+#         dummy = ( repeat( [ ipvt[ 0 : n ] ], n ) == transpose( repeat( [ range( n ) ], n ) )
+#         fjac = matrixmultiply( fjac, dummy )
+
+         ## Check for overflow.  This should be a cheap test here since FJAC
+         ## has been reduced to a (small) square matrix, and the test is
+         ## O(N^2).
+         #wh = where(finite(fjac) EQ 0, ct)
+         #if ct GT 0 then goto, FAIL_OVERFLOW
+
+# 20070130 HI: TODO - Find out if overflow check is necessary
+
+         ## Compute the norm of the scaled gradient
+         catch_msg = 'computing the scaled gradient'
+         gnorm = 0.
+         if ( self.fnorm != 0. ):
+            for j in range( n ):
+               l = ipvt[ j, 0 ]
+               if ( wa2[ l ] != 0. ):
+                  sum = ( fjac[ 0 : j + 1, j ] * qtf[ 0 : j + 1 ] ).sum() / self.fnorm
+                  gnorm = max( [ gnorm, abs( sum / wa2[ l ] ) ] )
+
+         ## Test for convergence of the gradient norm
+         if ( gnorm <= gtol ):
+            print('gnorm = ', gnorm)
+            self.status = 4
+            return
+
+         if ( maxiter == 0 ):
+            return
+
+         ## Rescale if necessary
+         if ( not rescale ):
+            wh = awhere( self.diag < wa2 )
+            self.diag = aput( self.diag, wh, aget( wa2, wh ) )
+
+         ## Beginning of the inner loop
+         while True:
+
+            ## Determine the levenberg-marquardt parameter
+            catch_msg = 'calculating LM parameter (MPFIT_)'
+            fjac, par, wa1, wa2 = self.lmpar( fjac, ipvt, self.diag, qtf,
+                                             wa1, wa2, delta, par )
+
+            ## Store the direction p and x+p. Calculate the norm of p
+            wa1 = -wa1
+
+            if ( not qanylim ) and ( not qminmax ) and ( not qanycyclic ):
+               ## No parameter limits, so just move to new position WA2
+               alpha = 1.
+               wa2 = x + wa1
+
+            else:
+      
+               ## Respect the limits.  If a step were to go out of bounds, then
+               ## we should take a step in the same direction but shorter distance.
+               ## The step should take us right to the limit in that case.
+               alpha = 1.
+
+               if qanylim:
+                  ## Do not allow any steps out of bounds
+                  catch_msg = 'checking for a step out of bounds'
+                  if ( nlpeg > 0 ):
+                     temp = aget( wa1, whlpeg )
+                     wa1 = aput( wa1, whlpeg, aput( temp, awhere( temp < 0. ), 0. ) )
+                  if ( nupeg > 0 ):
+                     temp = aget( wa1, whupeg )
+                     wa1 = aput( wa1, whupeg, aput( temp, awhere( temp > 0. ), 0. ) )
+
+                  dwa1 = ( abs( wa1 ) > machep )
+                  whl = awhere( dwa1 & qllim & ( ( x + wa1 ) < llim ) )
+                  if ( len( whl ) > 0 ):
+                     t = ( ( aget( llim, whl ) - aget( x, whl ) ) /
+                             aget( wa1, whl ) )
+                     alpha = min( [ alpha, t.min() ] )
+                  whu = awhere( dwa1 & qulim & ( ( x + wa1 ) > ulim ) )
+                  if ( len( whu ) > 0 ):
+                     t = ( ( aget( ulim, whu ) - aget( x, whu ) ) /
+                             aget( wa1, whu ) )
+                     alpha = min( [ alpha, t.min() ] )
+
+               ## Obey any max step values.
+               if qminmax:
+                 nwa1 = wa1 * alpha
+
+# 20070107 HI: Bug fix
+                 qmax2 = aget( qmax, ifree )
+                 maxstep2 = aget( maxstep, ifree )
+
+                 whmax = awhere( qmax2 & ( maxstep2 > 0. ) )
+                 if ( len( whmax ) > 0 ):
+                     mrat = ( abs( aget( nwa1, whmax ) ) / 
+                              abs( aget( maxstep2, whmax ) ) ).max()
+                     if ( mrat > 1. ):
+                        alpha = alpha / mrat
+
+               ## Scale the resulting vector
+               wa1 = wa1 * alpha
+               wa2 = x + wa1
+
+               ## Cycle around any cyclic parameter
+               if qanycyclic:
+                  wh = awhere( qcyclic & qulim & ( wa2 > ulim ) )
+                  if ( len( wh ) > 0 ):
+                     wa2 = aput( wa2, wh, aget( amodulo( wa2 - ulim, ulim - llim ) + llim, wh ) )
+                  wh = awhere( qcyclic & qulim & ( wa2 < llim ) )
+                  if ( len( wh ) > 0 ):
+                     wa2 = aput( wa2, wh, aget( amodulo( wa2 - llim, ulim - llim ) + llim, wh ) )
+
+               ## Adjust the final output values.  If the step put us exactly
+               ## on a boundary, make sure it is exact.
+               sgnu = array( ( ulim >= 0. ) * 2, dtype = self.dtype ) - 1.
+               sgnl = array( ( llim >= 0. ) * 2, dtype = self.dtype ) - 1.
+               wh = awhere( ( qcyclic == False ) & qulim & ( wa2 >= ulim * ( - sgnu * machep + 1. ) ) )
+               if ( len( wh ) > 0 ):
+                  wa2 = aput( wa2, wh, aget( ulim, wh ) )
+               wh = awhere( ( qcyclic == False ) & qllim & ( wa2 <= llim * ( - sgnl * machep + 1. ) ) )
+               if ( len( wh ) > 0 ):
+                  wa2 = aput( wa2, wh, aget( llim, wh ) )
+
+            wa3 = self.diag * wa1
+            pnorm = self.enorm( wa3 )
+
+            ## On the first iteration, adjust the initial step bound
+            if ( self.niter == 1 ):
+               delta = min( [ delta, pnorm ] )
+
+            self.params = aput( self.params, ifree, wa2 )
+
+# 20070131 HI: TODO - external
+ 
+            ## Evaluate the function at x+p and calculate its norm
+            catch_msg = 'calling ' + str( fcn )
+            self.status, wa4, dummy = self.call( fcn, self.params, functkw )
+            if ( self.status < 0 ):
+               self.errmsg = 'WARNING: premature termination by "' + fcn + '"'
+               return
+            fnorm1 = self.enorm( wa4 )
+  
+            ## Compute the scaled actual reduction
+            catch_msg = 'computing convergence criteria'
+            actred = - 1.
+            if ( ( 0.1 * fnorm1 ) < self.fnorm ):
+               actred = - ( fnorm1 / self.fnorm )**2 + 1.
+
+            ## Compute the scaled predicted reduction and the scaled directional
+            ## derivative
+            for j in range( n ):
+               wa3[ j ] = 0.
+               wa3[ 0 : j + 1 ] = wa3[ 0 : j + 1 ] + fjac[ 0 : j + 1, j ] * wa1[ ipvt[ j, 0 ] ]
+
+            ## Remember, alpha is the fraction of the full LM step actually
+            ## taken
+            temp1 = self.enorm( alpha * wa3 ) / self.fnorm
+            temp2 = ( sqrt( alpha * par ) * pnorm ) / self.fnorm
+            prered = temp1**2 + ( temp2**2 / 0.5 )
+            dirder = - ( temp1**2 + temp2**2 )
+
+            ## Compute the ratio of the actual to the predicted reduction.
+            ratio = 0.
+            if ( prered != 0. ):
+               ratio = actred / prered
+
+            ## Update the step bound
+            if ( ratio <= 0.25 ):
+               if ( actred >= 0. ):
+                  temp = 0.5
+               else:
+                  temp = ( 0.5 * dirder ) / ( dirder + 0.5 * actred )
+               if ( ( 0.1 * fnorm1 ) >= self.fnorm ) or ( temp < 0.1 ):
+                  temp = 0.1
+               delta = temp * min( [ delta, pnorm / 0.1 ] )
+               par = par / temp
+            else: 
+               if ( par == 0. ) or ( ratio >= 0.75 ):
+                  delta = pnorm / 0.5
+                  par = 0.5 * par
+
+            ## Test for successful iteration
+            if ( ratio >= 0.0001 ):
+               ## Successful iteration.  Update x, fvec, and their norms
+               x = wa2
+               wa2 = self.diag * x
+
+               fvec = wa4
+               xnorm = self.enorm( wa2 )
+               self.fnorm = fnorm1
+               self.niter = self.niter + 1
+ 
+            ## Tests for convergence
+            if ( ( abs( actred ) <= ftol ) and ( prered <= ftol )
+                  and ( 0.5 * ratio <= 1. ) ):
+               self.status = 1
+            if ( delta <= xtol * xnorm ):
+               self.status = 2
+            if ( ( abs( actred ) <= ftol ) and ( prered <= ftol )
+                  and ( 0.5 * ratio <= 1. ) and ( self.status == 2 ) ):
+               self.status = 3
+            if ( self.status != 0 ):
+               break
+
+            ## Tests for termination and stringent tolerances
+            if ( self.niter >= maxiter ):
+               self.status = 5
+            if ( ( abs( actred ) <= machep ) and ( prered <= machep ) 
+                and ( 0.5 * ratio <= 1. ) ):
+               self.status = 6
+            if ( delta <= machep * xnorm ):
+               self.status = 7
+            if ( gnorm <= machep ):
+               self.status = 8
+            if ( self.status != 0 ):
+               break
+
+            ## End of inner loop. Repeat if iteration unsuccessful
+            if ( ratio >= 0.0001 ):
+               break
+
+# 20070131 HI: TODO - overflow checks??
+
+         ## Check for over/underflow - SKIP FOR NOW
+         ##wh = where(finite(wa1) EQ 0 OR finite(wa2) EQ 0 OR finite(x) EQ 0, ct)
+         ##if ct GT 0 OR finite(ratio) EQ 0 then begin
+         ##   errmsg = ('ERROR: parameter or function value(s) have become '+$
+         ##      'infinite# check model function for over- '+$
+         ##      'and underflow')
+         ##   self.status = - 16
+         ##   break
+         if ( self.status != 0 ):
+            break;
+      ## End of outer loop.
+
+      catch_msg = 'in the termination phase'
+      ## Termination, either normal or user imposed.
+      if ( len( self.params ) == 0 ):
+         return
+      if ( nfree == 0 ):
+         self.params = xall.copy()
+      else:
+         self.params = aput( self.params, ifree, x )
+      dof = len( fvec ) - nfree
+
+
+      ## Call the ITERPROC at the end of the fit, if the fit status is
+      ## okay.  Don't call it if the fit failed for some reason.
+      if ( self.status > 0 ):
+
+         xnew0 = self.params.copy()
+
+         status = iterfunct( fcn, self.params, self.niter, self.fnorm**2, 
+            functkw = functkw, parinfo = parinfo, quiet = quiet, 
+            dof = dof, **iterkw )
+         if ( status != None ):
+            self.status = status
+
+         if ( self.status < 0 ):  
+            self.errmsg = 'WARNING: premature termination by ' + str( iterfunct )
+            return
+         ## If parameters were changed (grrr..) then re-tie
+         if ( ( abs( self.params - xnew0 ) ).sum() > 0. ):
+            if self.qanytied:
+               self.params = self.tie( self.params, ptied = ptied )
+            x = aget( self.params, ifree )
+
+      ## Initialize the number of parameters pegged at a hard limit value
+      npegged = 0
+      if qanylim:
+         wh = awhere( ( qulim & ( x == ulim ) ) |
+                        ( qllim & ( x == llim ) ) )
+         npegged = len( wh )
+
+# 20070131 HI: TODO - Add external to check
+
+      if ( nprint > 0 ) and ( self.status > 0 ):
+         catch_msg = 'calling ' + str( fcn )
+         status, fvec, dummy = self.call( fcn, self.params, functkw )
+         catch_msg = 'in the termination phase'
+         self.fnorm = self.enorm( fvec )
+
+      if ( self.fnorm != None ) and ( fnorm1 != None ):
+         self.fnorm = ( max( [ self.fnorm, fnorm1 ] ) )**2
+
+      self.covar = None
+      self.perror = None
+      ## (very carefully) set the covariance matrix COVAR
+      if ( ( self.status > 0 ) and ( not nocovar ) and ( n != None )
+                     and ( fjac != None ) and ( ipvt != None ) ):
+         sz = fjac.shape
+         if ( n > 0 ) and ( len( sz ) > 1 ):
+            if ( sz[ 0 ] >= n ) and ( sz[ 1 ] >= n ) and ( len( ipvt ) >= n ):
+               catch_msg = 'computing the covariance matrix'
+               cv = self.calc_covar( fjac[ 0 : n, 0 : n ], ipvt[ 0 : n, 0 ] )
+               cv = reshape( cv, ( n, n ) )
+               nn = len( xall )
+          
+               ## Fill in actual covariance matrix, accounting for fixed
+               ## parameters.
+               self.covar = zeros( shape = ( nn, nn ), dtype = self.dtype )
+               for i in range( n ):
+#                  indices = ifree + ifree[ i ] * nn
+#                  print self.covar.shape, ifree, indices, cv
+
+                  self.covar[ : , ifree[ i, 0 ] ] = aput( self.covar[ : , ifree[ i, 0 ] ], ifree, cv[ : , i ] )
+
+               ## Compute errors in parameters
+               catch_msg = 'computing parameter errors'
+               self.perror = zeros( shape = ( nn ), dtype = Float64 )
+               d = self.covar.diagonal()
+               wh = awhere( d >= 0. )
+               if ( len( wh ) > 0 ):
+                  self.perror = aput( self.perror, wh, sqrt( aget( d, wh ) ) )
+
+      return
+
+###############################################################################
+
+   ## Default procedure to be called every iteration.  It simply prints
+   ## the parameter values.
+   def defiter( self, fcn, x, iter, fnorm = None, functkw = None, 
+                      quiet = False, iterstop = None, parinfo = None, 
+                      format = None, pformat = '%.10g', dof = 1 ):
+
+      if self.debug:
+         print('Entering defiter...')
+
+      if not quiet:
+
+         if ( fnorm == None ):
+            status, fvec, dummy = self.call( fcn, x, functkw )
+            fnorm = ( self.enorm( fvec ) )**2
+   
+         ## Determine which parameters to print
+         nprint = len( x )
+   
+         print("Iter %6i   CHI-SQUARE = %.10g         DOF = %i" % ( iter, fnorm, dof ))
+         for i in range( nprint ):
+            if ( parinfo != None ):
+               if ( 'parname' in parinfo[ i ] ):
+                  p = '   ' + parinfo[i]['parname'] + ' = '
+               else:
+                  p = '   P' + str( i ) + ' = '
+               if ( 'mpprint' in parinfo[ i ] ):
+                  iprint = parinfo[ i ][ 'mpprint' ]
+               else:
+                  iprint = 1
+            else:
+               p = '   P' + str( i ) + ' = '
+               iprint = 1
+            if ( iprint > 0 ):
+               print(p + ( pformat % x[ i ] ) + '  ')
+
+         return 0
+
+###############################################################################
+
+   ##  DO_ITERSTOP:
+   ##  if keyword_set(iterstop) then begin
+   ##      k = get_kbrd(0)
+   ##      if k EQ string(byte(7)) then begin
+   ##          message, 'WARNING: minimization not complete', /info
+   ##          print, 'Do you want to terminate this procedure? (y/n)', $
+   ##            format='(A,$)'
+   ##          k = ''
+   ##          read, k
+   ##          if strupcase(strmid(k,0,1)) EQ 'Y' then begin
+   ##              message, 'WARNING: Procedure is terminating.', /info
+   ##              mperr = - 1
+   ##          endif
+   ##      endif
+   ##  endif
+
+###############################################################################
+
+   ## Procedure to parse the parameter values in PARINFO, which is a list of dictionaries
+   def parinfo( self, parinfo = None, key = 'a', default = None, n = 0 ):
+
+      if self.debug:
+         print('Entering parinfo...')
+
+      if ( ( n == 0 ) and ( parinfo != None ) ):
+         n = len( parinfo )
+
+      if ( n == 0 ):
+         values = default
+         return values
+
+      values = []
+      for i in range( n ):
+         if ( parinfo != None ):
+            if ( key in parinfo[ i ] ):
+               values.append( parinfo[ i ][ key ] )
+            else:
+               values.append( default )
+         else:
+            values.append( default )
+
+      # Convert to numpy arrays if possible
+      test = default
+      if ( type( default ) == type( [] ) ):
+         test = default[ 0 ]
+      if ( type( test ) == type( True ) ):
+         values = array( values, dtype = bool )
+      elif ( type( test ) == type( 1 ) ):
+         values = array( values, dtype = int32 )
+      elif ( type( test ) == type( 1. ) ):
+         values = array( values, dtype = self.dtype )
+
+      return values
+
+###############################################################################
+
+   ## Call user function or procedure, with _EXTRA or not, with
+   ## derivatives or not.
+   def call( self, fcn, x, functkw, dojac = None ):
+
+      if self.debug:
+         print('Entering call...')
+
+      if self.qanytied:
+         x = self.tie( x, ptied = self.ptied )
+
+      self.nfev = self.nfev + 1
+
+      status, f, temp = fcn( x, dojac = dojac, **functkw )
+
+      if ( dojac == None ):
+         fjac = None
+         if ( self.damp > 0. ):
+            ## Apply the damping if requested.  This replaces the residuals
+            ## with their hyperbolic tangent.  Thus residuals larger than
+            ## DAMP are essentially clipped.
+            f = tanh( f / self.damp )
+      else:
+          if sometrue( dojac ):
+             fjac = array( temp, dtype = self.dtype )
+             if ( fjac.shape != ( len( f ), len( x ) ) ):
+                status = - 1
+          else:
+             fjac = array( [[]], dtype = self.dtype )
+
+      return status, f, fjac
+
+###############################################################################
+
+   def enorm( self, vec ):
+
+      if self.debug:
+         print('Entering enorm...')
+
+      if self.__mpfit:
+        import _mpfit
+        ans = _mpfit.enorm( vec.tolist(), self.machar.rgiant, self.machar.rdwarf )
+        return ans
+
+
+      ## NOTE: it turns out that, for systems that have a lot of data
+      ## points, this routine is a big computing bottleneck.  The extended
+      ## computations that need to be done cannot be effectively
+      ## vectorized.  The introduction of the FASTNORM configuration
+      ## parameter allows the user to select a faster routine, which is 
+      ## based on TOTAL() alone.
+
+      # Very simple-minded sum-of-squares
+      if self.fastnorm:
+         ans = sqrt( ( vec**2 ).sum() )
+      else:
+         agiant = self.machar.rgiant / len( vec )
+         adwarf = self.machar.rdwarf * len( vec )
+
+         ## This is hopefully a compromise between speed and robustness.
+         ## Need to do this because of the possibility of over- or underflow.
+         mx = vec.max()
+         mn = vec.min()
+         mx = max( [ abs( mx ), abs( mn ) ] )
+         if ( mx == 0. ):
+            return 0.
+
+         if ( mx > agiant ) or ( mx < adwarf ):
+            ans = mx * sqrt( ( vec / mx )**2 )
+         else:
+            ans = sqrt( ( vec**2).sum() )
+
+      return ans
+
+###############################################################################
+
+   def fdjac2( self, fcn, x, fvec, step = None, ulimited = None, 
+              ulimit = None, dside = None,
+              epsfcn = None, autoderiv = True,
+              functkw = None, xall = None, ifree = None, dstep = None ):
+
+      if self.debug:
+         print('Entering fdjac2...')
+
+      machep = self.machar.machep
+
+      if ( epsfcn == None ):
+         epsfcn = machep
+      if ( xall == None ):
+          xall = x
+      if ( ifree == None ):
+         ifree = arange( len( xall ), dtype = int32 )
+      if ( step == None ):
+         step = azeros( x )
+      nall = len( xall )
+
+      eps = sqrt( max( [ epsfcn, machep ] ) )
+      m = len( fvec )
+      n = len( x )
+
+      ## Compute analytical derivative if requested
+      if ( not autoderiv ):
+         dojac = zeros( shape = ( nall ), dtype = bool )
+         dojac = aput( dojac, ifree, 1 )  ## Specify which parameters need derivatives
+         status, fp, fjac = self.call( fcn, xall, functkw, dojac = dojac )
+
+         if ( ( status < 0 ) or ( len( fjac.getflat() ) != m * nall ) ):
+             print('ERROR: Derivative matrix was not computed properly.')
+             return None
+
+         ## This definition is consistent with CURVEFIT
+         ## Sign error found (thanks Jesus Fernandez <fernande@irm.chu-caen.fr>)
+         fjac = reshape( - fjac, ( m, nall ) )
+
+         ## Select only the free parameters
+         if ( len( ifree ) < nall ):
+            temp = array( shape = ( m, n ), dtype = self.dtype )
+            for i in range( n ):
+               temp[ : , i ] = fjac[ : , ifree[ i, 0 ] ]
+            fjac = temp.copy()
+
+         return fjac
+
+      fjac = zeros( shape = ( m, n ), dtype = self.dtype )
+
+      h = eps * abs( x )
+
+      ## if STEP is given, use that
+      if ( step != None ):
+         stepi = aget( step, ifree )
+         wh = awhere( stepi > 0. )
+         if ( len( wh ) > 0 ):
+            h = aput( h, wh, aget( stepi, wh ) )
+
+      ## if relative step is given, use that
+      if ( len( dstep ) > 0 ):
+         dstepi = aget( dstep, ifree )
+         wh = awhere( dstepi > 0. )
+         if ( len( wh ) > 0 ):
+            h = aput( h, wh, aget( abs( dstepi * x ), wh ) )
+
+      ## In case any of the step values are zero
+      wh = awhere( h == 0. )
+      if ( len( wh ) > 0 ):
+         h = aput( h, wh, eps )
+
+      ## Reverse the sign of the step if we are up against the parameter
+      ## limit, or if the user requested it.
+
+# 20070201 HI: Bug fix
+      dside2 = aget( dside, ifree )
+#      mask = ( dside == - 1 )
+      mask = ( dside2 == - 1 )
+
+      if ( len( ulimited ) > 0 ) and ( len( ulimit ) > 0 ):
+         wh = awhere( mask | ( ulimited & ( x > ( ulimit - h ) ) ) )
+#        h = h * ( - mask * 2. + 1. )
+         if ( len( wh ) > 0 ):
+            h = aput( h, wh, aget( - h, wh ) )
+
+      ## Loop through parameters, computing the derivative for each
+      for j in range( n ):
+         xp = xall.copy()
+         xp[ ifree[ j, 0 ] ] = xp[ ifree[ j, 0 ] ] + h[ j ]
+
+         status, fp, dummy = self.call( fcn, xp, functkw )
+         if ( status < 0 ):
+            return None
+
+#         if ( abs( dside[ j ] ) <= 1 ):
+         if ( abs( dside2[ j ] ) <= 1 ):
+            ## COMPUTE THE ONE-SIDED DERIVATIVE
+            ## Note optimization fjac(0:*,j)
+            fjac[ 0 : , j ] = ( fp - fvec ) / h[ j ]
+#            fjac[ 0, j ] = ( fp - fvec ) / h[ j ]
+
+         else:
+            ## COMPUTE THE TWO-SIDED DERIVATIVE
+            xp[ ifree[ j, 0 ] ] = xall[ ifree[ j, 0 ] ] - h[ j ]
+
+            status, fm, dummy = self.call( fcn, xp, functkw )
+            if ( status < 0 ):
+               return None
+          
+            ## Note optimization fjac(0:*,j)
+            fjac[ 0 : , j ] = ( fp - fm ) / ( 2 * h[ j ] )
+#            fjac[ 0, j ] = ( fp - fm ) / ( 2 * h[ j ] )
+
+      return fjac
+
+###############################################################################
+
+   #     Original FORTRAN documentation
+   #     **********
+   #
+   #     subroutine qrfac
+   #
+   #     this subroutine uses householder transformations with column
+   #     pivoting (optional) to compute a qr factorization of the
+   #     m by n matrix a. that is, qrfac determines an orthogonal
+   #     matrix q, a permutation matrix p, and an upper trapezoidal
+   #     matrix r with diagonal elements of nonincreasing magnitude,
+   #     such that a*p = q*r. the householder transformation for
+   #     column k, k = 1,2,...,min(m,n), is of the form
+   #
+   #                        t
+   #        i - (1/u(k))*u*u
+   #
+   #     where u has zeros in the first k - 1 positions. the form of
+   #     this transformation and the method of pivoting first
+   #     appeared in the corresponding linpack subroutine.
+   #
+   #     the subroutine statement is
+   #
+   #    subroutine qrfac(m,n,a,lda,pivot,ipvt,lipvt,rdiag,acnorm,wa)
+   #
+   #     where
+   #
+   #    m is a positive integer input variable set to the number
+   #      of rows of a.
+   #
+   #    n is a positive integer input variable set to the number
+   #      of columns of a.
+   #
+   #    a is an m by n array. on input a contains the matrix for
+   #      which the qr factorization is to be computed. on output
+   #      the strict upper trapezoidal part of a contains the strict
+   #      upper trapezoidal part of r, and the lower trapezoidal
+   #      part of a contains a factored form of q (the non-trivial
+   #      elements of the u vectors described above).
+   #
+   #    lda is a positive integer input variable not less than m
+   #      which specifies the leading dimension of the array a.
+   #
+   #    pivot is a logical input variable. if pivot is set true,
+   #      then column pivoting is enforced. if pivot is set false,
+   #      then no column pivoting is done.
+   #
+   #    ipvt is an integer output array of length lipvt. ipvt
+   #      defines the permutation matrix p such that a*p = q*r.
+   #      column j of p is column ipvt(j) of the identity matrix.
+   #      if pivot is false, ipvt is not referenced.
+   #
+   #    lipvt is a positive integer input variable. if pivot is false,
+   #      then lipvt may be as small as 1. if pivot is true, then
+   #      lipvt must be at least n.
+   #
+   #    rdiag is an output array of length n which contains the
+   #      diagonal elements of r.
+   #
+   #    acnorm is an output array of length n which contains the
+   #      norms of the corresponding columns of the input matrix a.
+   #      if this information is not needed, then acnorm can coincide
+   #      with rdiag.
+   #
+   #    wa is a work array of length n. if pivot is false, then wa
+   #      can coincide with rdiag.
+   #
+   #     subprograms called
+   #
+   #    minpack-supplied ... dpmpar,enorm
+   #
+   #    fortran-supplied ... dmax1,dsqrt,min0
+   #
+   #     argonne national laboratory. minpack project. march 1980.
+   #     burton s. garbow, kenneth e. hillstrom, jorge j. more
+   #
+   #     **********
+
+   # NOTE: in IDL the factors appear slightly differently than described
+   # above.  The matrix A is still m x n where m >= n.  
+   #
+   # The "upper" triangular matrix R is actually stored in the strict
+   # lower left triangle of A under the standard notation of IDL.
+   #
+   # The reflectors that generate Q are in the upper trapezoid of A upon
+   # output.
+   #
+   #  EXAMPLE:  decompose the matrix [[9.,2.,6.],[4.,8.,7.]]
+   #    aa = [[9.,2.,6.],[4.,8.,7.]]
+   #    mpfit_qrfac, aa, aapvt, rdiag, aanorm
+   #     IDL> print, aa
+   #          1.81818*     0.181818*     0.545455*
+   #         -8.54545+      1.90160*     0.432573*
+   #     IDL> print, rdiag
+   #         -11.0000+     -7.48166+
+   #
+   # The components marked with a * are the components of the
+   # reflectors, and those marked with a + are components of R.
+   #
+   # To reconstruct Q and R we proceed as follows.  First R.
+   #    r = fltarr(m, n)
+   #    for i = 0, n - 1 do r(0:i,i) = aa(0:i,i)  # fill in lower diag
+   #    r(lindgen(n)*(m+1)) = rdiag
+   #
+   # Next, Q, which are composed from the reflectors.  Each reflector v
+   # is taken from the upper trapezoid of aa, and converted to a matrix
+   # via (I - 2 vT . v / (v . vT)).
+   #
+   #   hh = ident                                    ## identity matrix
+   #   for i = 0, n-1 do begin
+   #    v = aa(*,i) & if i GT 0 then v(0:i-1) = 0    ## extract reflector
+   #    hh = hh ## (ident - 2*(v # v)/total(v * v))  ## generate matrix
+   #   endfor
+   #
+   # Test the result:
+   #    IDL> print, hh ## transpose(r)
+   #          9.00000      4.00000
+   #          2.00000      8.00000
+   #          6.00000      7.00000
+   #
+   # Note that it is usually never necessary to form the Q matrix
+   # explicitly, and MPFIT does not.
+   
+###############################################################################
+
+   def qrfac( self, a, pivot = False ):
+
+      if self.debug:
+         print('Entering qrfac...')
+
+      if self.__mpfit:
+        import _mpfit
+        result = _mpfit.qrfac( a.tolist(), pivot, self.machar.machep, self.machar.rgiant, self.machar.rdwarf )
+        result = array( result )
+        m = result.shape[ 0 ] - 3
+        n = result.shape[ 1 ]
+        a = result[ 0 : m, : ]
+        ipvt = array( result[ m, : ] + 0.5, dtype = Int ) # only works cause ipvt >= 0
+        rdiag = result[ m + 1, : ]
+        acnorm = result[ m + 2, : ]
+        return a, ipvt.reshape( [ len( ipvt ), 1 ] ), rdiag, acnorm
+
+      machep = self.machar.machep
+      sz = a.shape
+      m = sz[ 0 ]
+      n = sz[ 1 ]
+
+      ## Compute the initial column norms and initialize arrays
+      acnorm = zeros( shape = ( n ), dtype = self.dtype )
+      for j in range( n ):
+         acnorm[ j ] = self.enorm( a[ : , j ] )
+      rdiag = acnorm.copy()
+      wa = rdiag.copy()
+      ipvt = arange( n ).reshape( [ n, 1 ] )
+
+      ## Reduce a to r with householder transformations
+      minmn = min( [ m, n ] )
+      for j in range( minmn ):
+         if pivot:
+
+            ## Bring the column of largest norm into the pivot position
+            rmax = ( rdiag[ j : ] ).max()
+            kmax = awhere( rdiag[ j : ] == rmax ) + j
+            if ( len( kmax ) > 0 ):
+               kmax = kmax[ 0, 0 ]
+         
+               ## Exchange rows via the pivot only.  Avoid actually exchanging
+               ## the rows, in case there is lots of memory transfer.  The
+               ## exchange occurs later, within the body of MPFIT, after the
+               ## extraneous columns of the matrix have been shed.
+               if ( kmax != j ):
+                  temp = ipvt[ j, 0 ]
+                  ipvt[ j, 0 ] = ipvt[ kmax, 0 ]
+                  ipvt[ kmax, 0 ] = temp
+                  rdiag[ kmax ] = rdiag[ j ]
+                  wa[ kmax ] = wa[ j ]
+
+         ## Compute the householder transformation to reduce the jth
+         ## column of A to a multiple of the jth unit vector
+         lj = ipvt[ j, 0 ]
+         ajj = a[ j :, lj ]
+         ajnorm = self.enorm( ajj )
+         if ( ajnorm == 0. ):
+            break
+         if ( a[ j, lj ] < 0. ):
+            ajnorm = - ajnorm
+         
+         ajj = ajj / ajnorm
+         ajj[ 0 ] = ajj[ 0 ] + 1
+         ## *** Note optimization a(j:*,j)
+         a[ j : , lj ] = ajj
+#         a[ j, lj ] = ajj
+
+         ## Apply the transformation to the remaining columns
+         ## and update the norms
+
+         ## NOTE to SELF: tried to optimize this by removing the loop,
+         ## but it actually got slower.  Reverted to "for" loop to keep
+         ## it simple.
+         if ( j + 1 < n ):
+            for k in range( j + 1, n ):
+               lk = ipvt[ k, 0 ]
+               ajk = a[ j : , lk ]
+               ## *** Note optimization a(j:*,lk) 
+               ## (corrected 20 Jul 2000)
+               if ( a[ j, lj ] != 0. ):
+                  a[ j : , lk ] = ajk - ajj * ( ajk * ajj ).sum() / a[ j, lj ]
+#                  a[ j, lk ] = ajk - ajj * ( ajk * ajj ).sum() / a[ j, lj ]
+
+                  if pivot and ( rdiag[ k ] != 0. ):
+                     temp = a[ j, lk ] / rdiag[ k ]
+                     rdiag[ k ] = rdiag[ k ] * sqrt( max( [ 1. - temp**2, 0. ] ) )
+                     temp = rdiag[ k ] / wa[ k ]
+                     if ( ( 0.05 * temp**2 ) <= machep ):
+                        rdiag[ k ] = self.enorm( a[ j + 1 : , lk ] )
+                        wa[ k ] = rdiag[ k ]
+         rdiag[ j ] = - ajnorm
+
+      return a, ipvt, rdiag, acnorm
+
+###############################################################################
+   
+   #     Original FORTRAN documentation
+   #     **********
+   #
+   #     subroutine qrsolv
+   #
+   #     given an m by n matrix a, an n by n diagonal matrix d,
+   #     and an m-vector b, the problem is to determine an x which
+   #     solves the system
+   #
+   #           a*x = b ,     d*x = 0 ,
+   #
+   #     in the least squares sense.
+   #
+   #     this subroutine completes the solution of the problem
+   #     if it is provided with the necessary information from the
+   #     factorization, with column pivoting, of a. that is, if
+   #     a*p = q*r, where p is a permutation matrix, q has orthogonal
+   #     columns, and r is an upper triangular matrix with diagonal
+   #     elements of nonincreasing magnitude, then qrsolv expects
+   #     the full upper triangle of r, the permutation matrix p,
+   #     and the first n components of (q transpose)*b. the system
+   #     a*x = b, d*x = 0, is then equivalent to
+   #
+   #                  t       t
+   #           r*z = q *b ,  p *d*p*z = 0 ,
+   #
+   #     where x = p*z. if this system does not have full rank,
+   #     then a least squares solution is obtained. on output qrsolv
+   #     also provides an upper triangular matrix s such that
+   #
+   #            t   t               t
+   #           p *(a *a + d*d)*p = s *s .
+   #
+   #     s is computed within qrsolv and may be of separate interest.
+   #
+   #     the subroutine statement is
+   #
+   #       subroutine qrsolv(n,r,ldr,ipvt,diag,qtb,x,sdiag,wa)
+   #
+   #     where
+   #
+   #       n is a positive integer input variable set to the order of r.
+   #
+   #       r is an n by n array. on input the full upper triangle
+   #         must contain the full upper triangle of the matrix r.
+   #         on output the full upper triangle is unaltered, and the
+   #         strict lower triangle contains the strict upper triangle
+   #         (transposed) of the upper triangular matrix s.
+   #
+   #       ldr is a positive integer input variable not less than n
+   #         which specifies the leading dimension of the array r.
+   #
+   #       ipvt is an integer input array of length n which defines the
+   #         permutation matrix p such that a*p = q*r. column j of p
+   #         is column ipvt(j) of the identity matrix.
+   #
+   #       diag is an input array of length n which must contain the
+   #         diagonal elements of the matrix d.
+   #
+   #       qtb is an input array of length n which must contain the first
+   #         n elements of the vector (q transpose)*b.
+   #
+   #       x is an output array of length n which contains the least
+   #         squares solution of the system a*x = b, d*x = 0.
+   #
+   #       sdiag is an output array of length n which contains the
+   #         diagonal elements of the upper triangular matrix s.
+   #
+   #       wa is a work array of length n.
+   #
+   #     subprograms called
+   #
+   #       fortran-supplied ... dabs,dsqrt
+   #
+   #     argonne national laboratory. minpack project. march 1980.
+   #     burton s. garbow, kenneth e. hillstrom, jorge j. more
+   #
+
+###############################################################################
+   
+   def qrsolv( self, r, ipvt, diag, qtb, sdiag ):
+
+      if self.debug:
+         print('Entering qrsolv...')
+
+      if self.__mpfit:
+        import _mpfit
+        result = _mpfit.qrsolv( r.tolist(), ipvt.ravel().tolist(), diag.tolist(), qtb.tolist(), sdiag.tolist() )
+        result = array( result )
+        m = result.shape[ 0 ] - 2
+        n = result.shape[ 1 ]
+        r = result[ 0 : m, : ]
+        x = result[ m, : ]
+        sdiag = result[ m + 1, : ]
+        return r, x, sdiag
+
+      sz = r.shape
+      m = sz[ 0 ]
+      n = sz[ 1 ]
+
+      ## copy r and (q transpose)*b to preserve input and initialize s.
+      ## in particular, save the diagonal elements of r in x.
+
+      for j in range( n ):
+         r[ j : n, j ] = r[ j, j : n ]
+      x = r.diagonal()
+      wa = qtb.copy()
+
+      ## Eliminate the diagonal matrix d using a givens rotation
+      for j in range( n ):
+         l = ipvt[ j, 0 ]
+         if ( diag[ l ] == 0. ):
+            break
+         sdiag[ j : ] = 0.
+         sdiag[ j ] = diag[ l ]
+
+         ## The transformations to eliminate the row of d modify only a
+         ## single element of (q transpose)*b beyond the first n, which
+         ## is initially zero.
+
+         qtbpj = 0.
+         for k in range( j, n ):
+            if ( sdiag[ k ] == 0. ):
+               break
+            if ( abs( r[ k, k ] ) < abs( sdiag[ k ] ) ):
+               cotan  = r[ k, k ] / sdiag[ k ]
+               sine   = 0.5 / sqrt( 0.25 + 0.25 * cotan**2 )
+               cosine = sine * cotan
+            else:
+               tang   = sdiag[ k ] / r[ k, k ]
+               cosine = 0.5 / sqrt( 0.25 + 0.25 * tang**2 )
+               sine   = cosine * tang
+
+            ## Compute the modified diagonal element of r and the
+            ## modified element of ((q transpose)*b,0).
+            r[ k, k ] = cosine * r[ k, k ] + sine * sdiag[ k ]
+            temp = cosine * wa[ k ] + sine * qtbpj
+            qtbpj = - sine * wa[ k ] + cosine * qtbpj
+            wa[ k ] = temp
+
+            ## Accumulate the transformation in the row of s
+            if ( n > k + 1 ):
+               temp = cosine * r[ k + 1 : n, k ] + sine * sdiag[ k + 1 : n ]
+               sdiag[ k + 1 : n ] = - sine * r[ k + 1 : n, k ] + cosine * sdiag[ k + 1 : n ]
+               r[ k + 1 : n, k ] = temp
+
+         sdiag[ j ] = r[ j, j ]
+         r[ j, j ] = x[ j ]
+
+      ## Solve the triangular system for z.  If the system is singular
+      ## then obtain a least squares solution
+      nsing = n
+      wh = awhere( sdiag == 0. )
+      if ( len( wh ) > 0 ):
+         nsing = wh[ 0 ]
+         wa[ nsing : ] = 0
+
+      if ( nsing >= 1 ):
+         wa[ nsing - 1 ] = wa[ nsing - 1 ] / sdiag[ nsing - 1 ] ## Degenerate case
+         ## *** Reverse loop ***
+         for j in range( nsing - 2, - 1, - 1 ):
+            sum = ( r[ j + 1 : nsing, j ] * wa[ j + 1 : nsing ]).sum()
+            wa[ j ] = ( wa[ j ] - sum ) / sdiag[ j ]
+
+      ## Permute the components of z back to components of x
+      x = aput( x, ipvt, wa )
+
+      return r, x, sdiag
+
+###############################################################################
+   
+   #     Original FORTRAN documentation
+   #
+   #     subroutine lmpar
+   #
+   #     given an m by n matrix a, an n by n nonsingular diagonal
+   #     matrix d, an m-vector b, and a positive number delta,
+   #     the problem is to determine a value for the parameter
+   #     par such that if x solves the system
+   #
+   #        a*x = b ,     sqrt(par)*d*x = 0 ,
+   #
+   #     in the least squares sense, and dxnorm is the euclidean
+   #     norm of d*x, then either par is zero and
+   #
+   #        (dxnorm-delta) .le. 0.1*delta ,
+   #
+   #     or par is positive and
+   #
+   #        abs(dxnorm-delta) .le. 0.1*delta .
+   #
+   #     this subroutine completes the solution of the problem
+   #     if it is provided with the necessary information from the
+   #     qr factorization, with column pivoting, of a. that is, if
+   #     a*p = q*r, where p is a permutation matrix, q has orthogonal
+   #     columns, and r is an upper triangular matrix with diagonal
+   #     elements of nonincreasing magnitude, then lmpar expects
+   #     the full upper triangle of r, the permutation matrix p,
+   #     and the first n components of (q transpose)*b. on output
+   #     lmpar also provides an upper triangular matrix s such that
+   #
+   #         t   t                   t
+   #        p *(a *a + par*d*d)*p = s *s .
+   #
+   #     s is employed within lmpar and may be of separate interest.
+   #
+   #     only a few iterations are generally needed for convergence
+   #     of the algorithm. if, however, the limit of 10 iterations
+   #     is reached, then the output par will contain the best
+   #     value obtained so far.
+   #
+   #     the subroutine statement is
+   #
+   #    subroutine lmpar(n,r,ldr,ipvt,diag,qtb,delta,par,x,sdiag,
+   #                     wa1,wa2)
+   #
+   #     where
+   #
+   #    n is a positive integer input variable set to the order of r.
+   #
+   #    r is an n by n array. on input the full upper triangle
+   #      must contain the full upper triangle of the matrix r.
+   #      on output the full upper triangle is unaltered, and the
+   #      strict lower triangle contains the strict upper triangle
+   #      (transposed) of the upper triangular matrix s.
+   #
+   #    ldr is a positive integer input variable not less than n
+   #      which specifies the leading dimension of the array r.
+   #
+   #    ipvt is an integer input array of length n which defines the
+   #      permutation matrix p such that a*p = q*r. column j of p
+   #      is column ipvt(j) of the identity matrix.
+   #
+   #    diag is an input array of length n which must contain the
+   #      diagonal elements of the matrix d.
+   #
+   #    qtb is an input array of length n which must contain the first
+   #      n elements of the vector (q transpose)*b.
+   #
+   #    delta is a positive input variable which specifies an upper
+   #      bound on the euclidean norm of d*x.
+   #
+   #    par is a nonnegative variable. on input par contains an
+   #      initial estimate of the levenberg-marquardt parameter.
+   #      on output par contains the final estimate.
+   #
+   #    x is an output array of length n which contains the least
+   #      squares solution of the system a*x = b, sqrt(par)*d*x = 0,
+   #      for the output par.
+   #
+   #    sdiag is an output array of length n which contains the
+   #      diagonal elements of the upper triangular matrix s.
+   #
+   #    wa1 and wa2 are work arrays of length n.
+   #
+   #     subprograms called
+   #
+   #    minpack-supplied ... dpmpar,enorm,qrsolv
+   #
+   #    fortran-supplied ... dabs,dmax1,dmin1,dsqrt
+   #
+   #     argonne national laboratory. minpack project. march 1980.
+   #     burton s. garbow, kenneth e. hillstrom, jorge j. more
+   #
+
+###############################################################################
+
+   def lmpar( self, r, ipvt, diag, qtb, x, sdiag, delta, par ):
+
+      if self.debug:
+         print('Entering lmpar...')
+
+      if self.__mpfit:
+        import _mpfit
+        result = _mpfit.lmpar( r.tolist(), ipvt.ravel().tolist(), diag.tolist(), qtb.tolist(), x.tolist(),
+            sdiag.tolist(), delta, par, self.machar.minnum, self.machar.rgiant, self.machar.rdwarf )
+        result = array( result )
+        m = result.shape[ 0 ] - 3
+        n = result.shape[ 1 ]
+        r = result[ 0 : m, : ]
+        par = result[ m, 0 ] # par is single float
+        x = result[ m + 1, : ]
+        sdiag = result[ m + 2, : ]
+        return r, par, x, sdiag
+
+      dwarf = self.machar.minnum
+      sz = r.shape
+      m = sz[ 0 ]
+      n = sz[ 1 ]
+
+      ## Compute and store in x the gauss-newton direction.  If the
+      ## jacobian is rank-deficient, obtain a least-squares solution
+      nsing = n
+      wa1 = qtb.copy()
+      wh = awhere( r.diagonal() == 0. )
+      if ( len( wh ) > 0 ):
+         nsing = wh[ 0, 0 ]
+         wa1[ nsing : ] = 0
+
+      if ( nsing >= 1 ):
+         ## *** Reverse loop ***
+         for j in range( nsing - 1, - 1, - 1 ):
+            wa1[ j ] = wa1[ j ] / r[ j, j ]
+            if ( j - 1 >= 0 ):
+               wa1[ 0 : j ] = wa1[ 0 : j ] - r[ 0 : j, j ] * wa1[ j ]
+
+      ## Note: ipvt here is a permutation array
+      x = aput( x, ipvt, wa1 )
+
+      ## Initialize the iteration counter.  Evaluate the function at the
+      ## origin, and test for acceptance of the gauss-newton direction
+      iter = 0
+      wa2 = diag * x
+      dxnorm = self.enorm( wa2 )
+      fp = dxnorm - delta
+      if ( fp <= 0.1 * delta ):
+         return r, 0., x, sdiag
+
+      ## If the jacobian is not rank deficient, the newton step provides a
+      ## lower bound, parl, for the zero of the function.  Otherwise set
+      ## this bound to zero.
+
+      parl = 0.
+      if ( nsing >= n ):
+         wa1 = aget( diag * wa2 / dxnorm, ipvt )
+
+         wa1[ 0 ] = wa1[ 0 ] / r[ 0, 0 ] ## Degenerate case 
+         for j in range( 1, n ):   ## Note "1" here, not zero
+            sum = ( r[ 0 : j, j ] * wa1[ 0 : j ] ).sum()
+            wa1[ j ] = ( wa1[ j ] - sum ) / r[ j, j ]
+
+         temp = self.enorm( wa1 )
+         parl = ( ( fp / delta ) / temp ) / temp
+
+      ## Calculate an upper bound, paru, for the zero of the function
+      for j in range( n ):
+         sum = ( r[ 0 : j + 1, j ] * qtb[ 0 : j + 1 ] ).sum()
+         wa1[ j ] = sum / diag[ ipvt[ j, 0 ] ]
+      gnorm = self.enorm( wa1 )
+      paru = gnorm / delta
+      if ( paru == 0. ):
+         paru = dwarf / min( [ delta, 0.1 ] )
+
+      ## If the input par lies outside of the interval (parl,paru), set
+      ## par to the closer endpoint
+
+      par = max( [ par, parl ] )
+      par = min( [ par, paru ] )
+      if ( par == 0. ):
+         par = gnorm / dxnorm
+
+      ## Beginning of an interation
+      while( True ):
+         iter = iter + 1
+      
+         ## Evaluate the function at the current value of par
+         if ( par == 0. ):
+            par = max( [ dwarf, paru * 0.001 ] )
+         temp = sqrt( par )
+         wa1 = temp * diag
+         r, x, sdiag = self.qrsolv( r, ipvt, wa1, qtb, sdiag )
+         wa2 = diag * x
+         dxnorm = self.enorm( wa2 )
+         temp = fp
+         fp = dxnorm - delta
+
+         if ( ( abs( fp ) <= 0.1 * delta ) or
+              ( ( parl == 0. ) and ( fp <= temp ) and ( temp < 0 ) ) or
+              ( iter == 10 ) ):
+            break;
+
+         ## Compute the newton correction
+         wa1 = aget( diag * wa2 / dxnorm, ipvt )
+
+         for j in range( n - 1 ):
+            wa1[ j ] = wa1[ j ] / sdiag[ j ]
+            wa1[ j + 1 : n ] = wa1[ j + 1 : n ] - r[ j + 1 : n, j ] * wa1[ j ]
+         wa1[ n - 1 ] = wa1[ n - 1 ] / sdiag[ n - 1 ] ## Degenerate case
+
+         temp = self.enorm( wa1 )
+         parc = ( fp / delta ) / ( temp**2 )
+
+         ## Depending on the sign of the function, update parl or paru
+         if ( fp > 0. ):
+            parl = max( [ parl, par ] )
+         if ( fp < 0. ):
+            paru = min( [ paru, par ] )
+
+         ## Compute an improved estimate for par
+         par = max( [ parl, par + parc ] )
+
+         ## End of an iteration
+
+      ## Termination
+      return r, par, x, sdiag
+
+###############################################################################
+   
+   ## Procedure to tie one parameter to another.
+   def tie( self, p, ptied = None ):
+
+      if self.debug:
+         print('Entering tie...')
+
+      pp = p.copy()
+      if ( ptied != None ):
+         for i in range( len( ptied ) ):
+            if ( ptied[ i ] != '' ):
+               cmd = 'pp[' + str( i ) + '] = ' + ptied[ i ]
+               exec( cmd )
+
+      return pp
+
+###############################################################################
+   
+   #     Original FORTRAN documentation
+   #     **********
+   #
+   #     subroutine covar
+   #
+   #     given an m by n matrix a, the problem is to determine
+   #     the covariance matrix corresponding to a, defined as
+   #
+   #                    t
+   #           inverse(a *a) .
+   #
+   #     this subroutine completes the solution of the problem
+   #     if it is provided with the necessary information from the
+   #     qr factorization, with column pivoting, of a. that is, if
+   #     a*p = q*r, where p is a permutation matrix, q has orthogonal
+   #     columns, and r is an upper triangular matrix with diagonal
+   #     elements of nonincreasing magnitude, then covar expects
+   #     the full upper triangle of r and the permutation matrix p.
+   #     the covariance matrix is then computed as
+   #
+   #                      t     t
+   #           p*inverse(r *r)*p  .
+   #
+   #     if a is nearly rank deficient, it may be desirable to compute
+   #     the covariance matrix corresponding to the linearly independent
+   #     columns of a. to define the numerical rank of a, covar uses
+   #     the tolerance tol. if l is the largest integer such that
+   #
+   #           abs(r(l,l)) .gt. tol*abs(r(1,1)) ,
+   #
+   #     then covar computes the covariance matrix corresponding to
+   #     the first l columns of r. for k greater than l, column
+   #     and row ipvt(k) of the covariance matrix are set to zero.
+   #
+   #     the subroutine statement is
+   #
+   #       subroutine covar(n,r,ldr,ipvt,tol,wa)
+   #
+   #     where
+   #
+   #       n is a positive integer input variable set to the order of r.
+   #
+   #       r is an n by n array. on input the full upper triangle must
+   #         contain the full upper triangle of the matrix r. on output
+   #         r contains the square symmetric covariance matrix.
+   #
+   #       ldr is a positive integer input variable not less than n
+   #         which specifies the leading dimension of the array r.
+   #
+   #       ipvt is an integer input array of length n which defines the
+   #         permutation matrix p such that a*p = q*r. column j of p
+   #         is column ipvt(j) of the identity matrix.
+   #
+   #       tol is a nonnegative input variable used to define the
+   #         numerical rank of a in the manner described above.
+   #
+   #       wa is a work array of length n.
+   #
+   #     subprograms called
+   #
+   #       fortran-supplied ... dabs
+   #
+   #     argonne national laboratory. minpack project. august 1980.
+   #     burton s. garbow, kenneth e. hillstrom, jorge j. more
+   #
+   #     **********
+
+###############################################################################
+   
+   def calc_covar( self, rr, ipvt = None, tol = 1.e-14 ):
+
+      if self.debug:
+         print('Entering calc_covar...')
+
+      if self.__mpfit:
+        import _mpfit
+        r = _mpfit.calc_covar( rr.tolist(), ipvt.ravel().tolist(), tol )
+        return array( r )
+
+      s = rr.shape
+      if ( rank( rr ) != 2 ):
+         print('ERROR: r must be a two-dimensional matrix')
+         return - 1
+      m = s[ 0 ]
+      n = s[ 1 ]
+      if ( m != n ):
+         print('ERROR: r must be a square matrix')
+         return - 1
+
+      if ( ipvt == None ):
+         ipvt = arange( n ).reshape( [ n, 1 ] )
+      r = rr.copy()
+      r = reshape( r, ( n, n ) )
+
+      ## Form the inverse of r in the full upper triangle of r
+      l = - 1
+      tolr = tol * abs( r[ 0, 0 ] ) # BUG!! because r[ 0, 0 ] might be 0.
+      for k in range( n ):
+         if ( abs( r[ k, k ] ) <= tolr ):
+            break
+         r[ k, k ] = 1. / r[ k, k ]
+         for j in range( k ):
+            temp = r[ k, k ] * r[ j, k ]
+            r[ j, k ] = 0.
+            r[ 0 : j + 1, k ] = r[ 0 : j + 1, k ] - temp * r[ 0 : j + 1, j ]
+         l = k
+      return r
+
+      ## Form the full upper triangle of the inverse of (r transpose)*r
+      ## in the full upper triangle of r
+      if l >= 0:
+         for k in range( l + 1 ):
+            for j in range( k ):
+               temp = r[ j, k ]
+               r[ 0 : j + 1 , j ] = r[ 0 : j + 1, j ] + temp * r[ 0 : j + 1, k ]
+            temp = r[ k, k ]
+            r[ 0 : k + 1, k ] = temp * r[ 0 : k + 1, k ]
+
+      ## Form the full lower triangle of the covariance matrix
+      ## in the strict lower triangle or and in wa
+      wa = repeat( [ r[ 0, 0 ] ], n )
+      for j in range( n ):
+         jj = ipvt[ j, 0 ]
+         sing = ( j > l )
+         for i in range( j + 1 ):
+             if sing:
+                r[ i, j ] = 0.
+             ii = ipvt[ i, 0 ]
+             if ( ii > jj ):
+                r[ ii, jj ] = r[ i, j ]
+             if ( ii < jj ):
+                r[ jj, ii ] = r[ i, j ]
+         wa[ jj ] = r[ j, j ]
+
+      ## Symmetrize the covariance matrix in r
+      for j in range( n ):
+         r[ 0 : j + 1, j ] = r[ j, 0 : j + 1 ]
+         r[ j, j ] = wa[ j ]
+
+      return r
+
+###############################################################################
+
+class machar:
+
+   def __init__( self, double = True ):
+
+      if not double:
+         self.machep = 1.19209e-007
+         self.maxnum = 3.40282e+038
+         self.minnum = 1.17549e-038
+      else:
+         self.machep = 2.2204460e-016
+         self.maxnum = 1.7976931e+308
+         self.minnum = 2.2250739e-308
+      self.maxgam = 171.624376956302725
+      self.maxlog = log( self.maxnum )
+      self.minlog = log( self.minnum )
+      self.rdwarf = sqrt( self.minnum * 1.5 ) * 10.
+      self.rgiant = sqrt( self.maxnum ) * 0.1
+
+###############################################################################
diff --git a/CEP/Calibration/ExpIon/src/parmdbmain.py b/CEP/Calibration/ExpIon/src/parmdbmain.py
new file mode 100644
index 0000000000000000000000000000000000000000..05eb57b3ef452600db9b5c61b93289252283b8b6
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/parmdbmain.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2007
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id$
+
+import subprocess
+
+def store_parms( pdbname, parms, create_new = False) : 
+
+   FNULL = open( '/dev/null', 'w' )
+   process = subprocess.Popen( ['parmdbm'], shell = False, stdin = subprocess.PIPE, stdout = FNULL, stderr = FNULL )
+   if create_new :
+      process.stdin.write( "create tablename='" + pdbname + "'\n" )
+   else : 
+      process.stdin.write( "open tablename='" + pdbname + "'\n" )
+
+   parmnames = list(parms.keys())
+   for parmname in parmnames:
+      v = parms[parmname]
+      times = v['times']
+      nt = len(times)
+      freqs = v['freqs']
+      nf = len(freqs)
+      timewidths = v['timewidths']
+      freqwidths = v['freqwidths']
+      values = v['values']
+      repr_values = '[' + ', '.join([repr(v1) for v1 in values.flat]) + ']'
+      freq_start = freqs[0]-freqwidths[0]/2
+      freq_end = freqs[-1]+freqwidths[-1]/2
+      time_start = times[0] - timewidths[0]/2
+      time_end = times[-1] + timewidths[-1]/2
+      domain = "[%s,%s,%s,%s]" % ( freq_start, freq_end, time_start, time_end )
+      process.stdin.write("add %s shape=[%i,%i], values=%s, domain=%s\n" % (parmname, nf, nt, repr_values, domain))
+
+   process.stdin.write('quit\n')
+   process.wait()
diff --git a/CEP/Calibration/ExpIon/src/parmdbwriter.py b/CEP/Calibration/ExpIon/src/parmdbwriter.py
new file mode 100755
index 0000000000000000000000000000000000000000..43a90cf0126caa1271bbf8b7af2e43267ce54be1
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/parmdbwriter.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+
+import lofar.parmdb
+import lofar.expion.parmdbmain
+import sys
+import numpy
+
+rank = sys.argv[1]
+instrument_pdbname = sys.argv[2]
+globaldbname = sys.argv[3]
+ion_pdbname = sys.argv[4]
+
+ion_pdbname = '/'.join(instrument_pdbname.split('/')[0:-1]) + '/' + ion_pdbname
+
+instrument_pdb = lofar.parmdb.parmdb( instrument_pdbname )
+ClockTEC_pdb = lofar.parmdb.parmdb( globaldbname + "/ionosphere")
+
+instrument_parms = instrument_pdb.getValuesGrid('Gain:{0:0,1:1}:*')
+Clock_parms = ClockTEC_pdb.getValuesGrid('Clock*')
+TEC_parms = ClockTEC_pdb.getValuesGrid('TEC*')
+
+ionosphere_parms = instrument_parms
+for parm_name in list(Clock_parms.keys()) :
+   parm_name_split = parm_name.split(':')
+   pol = parm_name_split[1]
+   station = parm_name_split[2]
+   try :
+      Clock = Clock_parms[ ':'.join( [ 'Clock', pol, station ] ) ][ 'values' ]
+      TEC = TEC_parms[ ':'.join( [ 'TEC', pol, station ] ) ][ 'values' ]
+   except KeyError :
+      pass
+   else:
+      parm_real = ionosphere_parms[ ':'.join( [ 'Gain', pol, pol, 'Real', station] ) ]
+      parm_imag = ionosphere_parms[ ':'.join( [ 'Gain', pol, pol, 'Imag', station] ) ]
+      freqs = parm_real['freqs']
+      phases = Clock*freqs*2*numpy.pi/1e9 + TEC*8e9/freqs
+      phasors = numpy.sqrt(parm_real['values']**2 + parm_imag['values']**2) * numpy.exp(1j * phases)
+      parm_real[ 'values' ] = phasors.real
+      parm_imag[ 'values' ] = phasors.imag
+
+lofar.expion.parmdbmain.store_parms( ion_pdbname, ionosphere_parms, create_new = True )
+
+
+
diff --git a/CEP/Calibration/ExpIon/src/read_sagecal.py b/CEP/Calibration/ExpIon/src/read_sagecal.py
new file mode 100644
index 0000000000000000000000000000000000000000..4a0fbb73bec59ba1e35ad0daa2a9c4fbb34f9ccc
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/read_sagecal.py
@@ -0,0 +1,223 @@
+import numpy as np
+import tables as tab
+import os
+from subprocess import call
+
+def getClusters(clusterf,skymodel,max_nr_clusters=1000):
+    #get clusters
+    sky=open(skymodel)
+    sources={}
+    for line in sky:
+        if line.strip()[0]=='#':
+            continue;
+        splitted=line.split()
+        sources[splitted[0].strip()]={}
+        sources[splitted[0].strip()]['Ra']=np.pi/12.*(float(splitted[1])
+                                                      +float(splitted[2])/60.
+                                                      +float(splitted[3])/3600.)
+        sources[splitted[0].strip()]['Dec']=np.pi/180.*(float(splitted[4])
+                                                        +float(splitted[5])/60.
+                                                        +float(splitted[6])/3600.)
+        sources[splitted[0].strip()]['I']=float(splitted[7])
+        sources[splitted[0].strip()]['sp']=float(splitted[11])
+        sources[splitted[0].strip()]['freq0']=float(splitted[-1])
+
+    clusterfile=open(clusterf)
+    clusters=[]
+    count=0
+    tot_nr_sol=0
+    nrSB=0
+    for line in clusterfile:
+        print("adding cluster",line)
+        if line.strip()[0]=='#':
+            continue;
+        splitted=line.split()
+        if count>=max_nr_clusters:
+            tot_nr_sol+=int(splitted[1])
+            continue
+        clusters.append({})
+        clusters[count]['id']=int(splitted[0])
+        clusters[count]['nrsol']=int(splitted[1])
+        clusters[count]['real']=[]
+        clusters[count]['imag']=[]
+        clusters[count]['sources']={}
+        clusters[count]['store_data']=True
+        avg_ra=0
+        avg_dec=0
+        sum_weight=0
+        for src in splitted[2:]:
+            clusters[count]['sources'][src.strip()]=sources[src.strip()]
+            weight=sources[src.strip()]['I']
+            avg_ra+=sources[src.strip()]['Ra']*weight                
+            avg_dec+=sources[src.strip()]['Dec']*weight
+            sum_weight+=weight
+        clusters[count]['Ra']=avg_ra/sum_weight
+        clusters[count]['Dec']=avg_dec/sum_weight
+        tot_nr_sol+=clusters[count]['nrsol']
+        count+=1
+
+    return clusters,tot_nr_sol
+
+def get_freq_data(sol,clusters,tot_nr_sol):
+    data=[]
+    indices=[]
+    for line in sol:
+        splitted=line.split()
+        indices.append(int(splitted[0]))
+        data.append(np.array([float(i) for i in splitted[1:]]))
+    data=np.array(data);
+    nrStations=(max(indices)+1)/8
+    nrTimes=data.shape[0]/(8*nrStations)
+    print(data.shape,nrTimes,nrStations,8)
+    if data.shape[0]!=nrTimes*nrStations*8:
+        print("wrong shape")
+        return -1
+    data=data.reshape(nrTimes,nrStations,8,tot_nr_sol)
+    start=0
+    for icl,cluster in enumerate(clusters):
+        if cluster['store_data']==True:
+            cluster['real'].append(data[:,:,0:8:2,start:start+cluster['nrsol']])
+            cluster['imag'].append(data[:,:,1:8:2,start:start+cluster['nrsol']])
+            start+=cluster['nrsol']
+    return 1
+
+def remove_unitary(clusters,freqs,store_intermediate=False):
+    for idxc,cluster in enumerate(clusters):
+      if cluster['store_data']:
+        if store_intermediate:
+            first=True
+            for isb in freqs:
+                data=np.load('tmp_store_real_%d_%d.npy'%(idxc,isb))
+                if first:
+                    cluster['real']=data
+                else:
+                    cluster['real']=np.concatenate((cluster['real'],data))
+                data=np.load('tmp_store_imag_%d_%d.npy'%(idxc,isb))
+                if first:
+                    cluster['imag']=data
+                    first=False
+                else:
+                    cluster['imag']=np.concatenate((cluster['imag'],data))
+                call("rm tmp_store_real_%d_%d.npy"%(idxc,isb),shell=True)
+                call("rm tmp_store_imag_%d_%d.npy"%(idxc,isb),shell=True)
+
+                    
+        else:
+            cluster['real']=np.array(cluster['real'])
+            cluster['imag']=np.array(cluster['imag'])
+        nrTimes=cluster['real'].shape[1]
+        nrSB=cluster['real'].shape[0]
+        nrStations=cluster['real'].shape[2]
+        cdata=cluster['real']+1.j*cluster['imag']
+        cluster['real']=[]
+        cluster['imag']=[]
+        print(cdata.shape)
+        cdata=np.swapaxes(cdata,0,1)
+        print(cdata.shape)
+        cdata=np.swapaxes(cdata,3,4)
+        print(cdata.shape)
+        cdata=np.swapaxes(cdata,2,3)
+        print(cdata.shape)
+        cdata=np.swapaxes(cdata,1,2)
+        print(cdata.shape,nrTimes*cluster['nrsol'],nrSB,nrStations,4)
+        cdata=cdata.reshape(nrTimes*cluster['nrsol'],nrSB,nrStations,4)
+
+
+        #multiply with unitary matrix to get something more constant in time/freq
+        
+        J0=cdata[0,0].reshape(nrStations*2,2)
+        for ntime in range(nrTimes*cluster['nrsol']):
+            for nfreq in range(nrSB):
+                if ntime==0 and nfreq==0:
+                    continue;
+                J1=cdata[ntime,nfreq].reshape(nrStations*2,2)
+                u,s,v=np.linalg.svd(np.dot(np.conjugate(J1.T),J0))
+                U1=np.dot(u,v)
+                J0=np.dot(J1,U1)
+                cdata[ntime,nfreq]=J0.reshape(nrStations,4)
+            J0=cdata[ntime,0].reshape(nrStations*2,2)
+        if store_intermediate:
+            np.save('tmp_store_cdata_%d.npy'%(idxc),cdata)
+        else:
+            cluster['cdata']=cdata
+
+def fill_sb(clusters,solpath,solpath_end,subbandlist,tot_nr_sol,store_intermediate=False):
+    freqs=[]
+    for isb,sb in enumerate(subbandlist):
+        print("opening",solpath+str(sb)+solpath_end)
+        if not os.path.isfile(solpath+str(sb)+solpath_end):
+            print("skipping",sb)
+            continue;
+        sol=open(solpath+str(sb)+solpath_end)
+        if get_freq_data(sol,clusters,tot_nr_sol)>0:
+            freqs.append(isb)
+            # store intermediate results....to save memory?
+            if store_intermediate:
+                for idxc,cluster in enumerate(clusters):
+                    if cluster['store_data']:
+                        np.save('tmp_store_real_%d_%d.npy'%(idxc,isb),np.array(cluster['real']))
+                        np.save('tmp_store_imag_%d_%d.npy'%(idxc,isb),np.array(cluster['imag']))
+                        cluster['real']=[]
+                        cluster['imag']=[]
+            
+    return freqs
+        
+def addToH5File(h5file,clusters,freqs,store_intermediate=False):
+    #group into nrsolutions
+    poss_nr_sol=[]
+    groups=[]
+    for clusteridx,cluster in enumerate(clusters):
+        if not cluster['nrsol'] in poss_nr_sol:
+            poss_nr_sol.append(cluster['nrsol'])
+            groups.append([])
+        idx=poss_nr_sol.index(cluster['nrsol'])
+        groups[idx].append(clusteridx)
+
+    if 'sagefreqIdx' in h5file.root:
+        h5file.removeNode('/sagefreqIdx')
+
+    h5file.createArray(h5file.root, 'sagefreqIdx',freqs)
+    for igrp,grp in enumerate(groups):
+        # create arrays:
+        if store_intermediate:
+            cdata=np.load('tmp_store_cdata_%d.npy'%(grp[0]))
+        else:
+            cdata=clusters[grp[0]]['cdata']
+            
+        arrayshape=cdata.shape[:-1]+(len(grp),4)
+        for name in ['sageradec%d'%igrp,'sagephases%d'%igrp,'sageamplitudes%d'%igrp]:
+            if name in h5file.root:
+                h5file.removeNode('/'+name)
+        
+        srcarray = h5file.createCArray(h5file.root, 'sageradec%d'%igrp, tab.Float32Atom(), shape=(len(grp),2))
+        pharray = h5file.createCArray(h5file.root, 'sagephases%d'%igrp, tab.Float32Atom(), shape=arrayshape)
+        amparray = h5file.createCArray(h5file.root, 'sageamplitudes%d'%igrp, tab.Float32Atom(), shape=arrayshape)
+        for idx,clusteridx  in enumerate(grp):
+            if store_intermediate:
+                cdata=np.load('tmp_store_cdata_%d.npy'%(clusteridx))
+            else:
+                cdata=clusters[clusteridx]['cdata']
+            pharray[:,:,:,idx,:]=np.angle(cdata)
+            amparray[:,:,:,idx,:]=np.absolute(cdata)
+            srcarray[idx,:]=np.array([clusters[clusteridx]['Ra'],clusters[clusteridx]['Dec']])
+            clusters[clusteridx]['cdata']=[]
+            if store_intermediate:
+                call("rm tmp_store_cdata_%d.npy"%(clusteridx),shell=True)
+        pharray.flush();
+        amparray.flush();
+        srcarray.flush();
+
+def UpdateIonmodel(h5filename,clusterf,skymodel,solpath,subbands,max_nr_clusters=1000,store_intermediate=True):
+    '''Add sagecal solutions to your ionmodel.hdf5.Use store_intermediate if you are trying to store many solutions, since otherwise the program will run out of memory. subbands is a list with subband indices. The sagecal solutions are stored per group with the same timestep. THe list of valid subband indices are also stored in the hdf5 file.''' 
+    
+    file_example=os.listdir(solpath)[0]
+    pos=file_example.find('SB')
+    start_name=solpath+'/'+file_example[:pos+2]
+    end_name=file_example[pos+5:]
+    subbandlist=['%03d'%(sb) for sb in subbands]
+    clusters,solshape=getClusters(clusterf=clusterf,skymodel=skymodel,max_nr_clusters=max_nr_clusters)
+    freqs=fill_sb(clusters,solpath=start_name,solpath_end=end_name,subbandlist=subbandlist,tot_nr_sol=solshape,store_intermediate=store_intermediate)
+    remove_unitary(clusters,freqs,store_intermediate)
+    h5file = tab.openFile(h5filename, mode = "r+")
+    addToH5File(h5file,clusters,np.array(subbands)[freqs],store_intermediate=store_intermediate)
+    
diff --git a/CEP/Calibration/ExpIon/src/readms-part.py b/CEP/Calibration/ExpIon/src/readms-part.py
new file mode 100755
index 0000000000000000000000000000000000000000..0e20fa37483093595840bbb6f4e1d336aa17d7a6
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/readms-part.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+
+import os
+import socket
+import sys
+import pyrap.tables as pt
+
+
+masterhost = sys.argv[2]
+port = int( sys.argv[3] )
+partnr = sys.argv[5]
+msname = sys.argv[6]
+
+antenna_table = pt.table( msname + "/ANTENNA")
+name_col = antenna_table.getcol('NAME')
+position_col = antenna_table.getcol( 'POSITION' )
+antenna_table.close()
+
+field_table = pt.table( msname + "/FIELD")
+phase_dir_col = field_table.getcol('PHASE_DIR')
+field_table.close()
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect((masterhost, port))
+s.sendall(partnr.encode("ascii"))
+s.recv(1024)
+s.sendall(repr(name_col).encode("ascii"))
+s.recv(1024)
+s.sendall(repr(position_col).encode("ascii"))
+s.recv(1024)
+s.sendall(repr(phase_dir_col).encode("ascii"))
+s.recv(1024)
+
+s.close()
diff --git a/CEP/Calibration/ExpIon/src/readms-part.sh b/CEP/Calibration/ExpIon/src/readms-part.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e4f916f81a29dc7ea3b1773d0571c4e1d26be11f
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/readms-part.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+source $9
+
+cd ${10}
+
+exec `dirname $0`/readms-part.py $@
+
diff --git a/CEP/Calibration/ExpIon/src/readms.py b/CEP/Calibration/ExpIon/src/readms.py
new file mode 100755
index 0000000000000000000000000000000000000000..4385626bd7153208f18114ace91179a10df085b1
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/readms.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2007
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id$
+
+
+import os
+import socket
+import sys
+import time
+import numpy
+
+from threading import Thread
+
+class Server( Thread ):
+
+   def __init__ ( self ):
+      Thread.__init__( self )
+      self.socket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
+      self.port = 2727
+      while True:
+         try:
+            self.socket.bind ( ( '', self.port ) )
+            break
+         except socket.error:
+            self.port += 1
+            
+      self.socket.listen ( 3 )
+      self.socket.settimeout(1)
+
+   def run(self):
+      self.connectionserverlist = []
+      self.stop_flag = False
+      while not self.stop_flag:
+         try:
+            connection, details = self.socket.accept()
+         except socket.timeout:
+            pass
+         else:
+            connection.settimeout( None )
+            connectionserver = ConnectionServer(connection)
+            connectionserver.start()
+            self.connectionserverlist.append( connectionserver )
+   
+   def get_results( self ):
+      
+      self.stop_flag = True
+      self.join()
+      results = []
+      for connectionserver in self.connectionserverlist:
+         connectionserver.join()
+         results.append( connectionserver.result )
+      return results
+      
+   
+class ConnectionServer( Thread ):
+   
+   def __init__( self, connection ):
+      Thread.__init__( self )
+      self.connection = connection
+      
+   def run( self ):
+      self.result = []
+      while True:
+         data = self.connection.recv(1024)
+         if not data:
+            break
+         #Always explicitely send an aknowledgement, this speeds up communication
+         self.connection.send('OK')
+         self.result.append( data )
+      self.connection.close()
+
+
+def readms(gdsfile, clusterdesc):
+
+   host = socket.gethostname()
+
+   server = Server()
+   server.start()
+   os.system("startdistproc -useenv -wait -cdn %s -dsn %s -mode %i -masterhost %s -nostartmaster `which readms-part.sh` $PWD" % (clusterdesc, gdsfile, server.port, host))
+
+   results = server.get_results()
+   antennas = eval(results[0][1], {})
+   positions = eval(results[0][2], {'array': numpy.array})
+   pointing = eval(results[0][3], {'array': numpy.array})
+   
+   return (antennas, positions, pointing)
+   
diff --git a/CEP/Calibration/ExpIon/src/repairGlobaldb.py b/CEP/Calibration/ExpIon/src/repairGlobaldb.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ee4a8d8898b9285ff75f079f00b89da1ee8ddac
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/repairGlobaldb.py
@@ -0,0 +1,361 @@
+import tables
+import lofar.parmdb
+import pyrap.tables as pt
+import lofar.expion.ionosphere as iono
+import os
+import numpy as np
+class dummyion:
+    pass
+
+def get_source_list( pdb, source_pattern_list ):
+   source_list = []
+   for pattern in source_pattern_list :
+      parmname_list = pdb.getNames( 'DirectionalGain:?:?:*:*:' + pattern )
+      source_list.extend([n.split(':')[-1] for n in parmname_list])
+      parmname_list = pdb.getNames( 'RotationAngle:*:' + pattern )
+      source_list.extend([n.split(':')[-1] for n in parmname_list])
+      parmname_list = pdb.getNames( 'ScalarPhase:*:' + pattern )
+      source_list.extend([n.split(':')[-1] for n in parmname_list])
+   print(set(source_list))
+   return sorted(set(source_list))
+
+def get_station_list( pdb, station_pattern_list, DirectionalGainEnable ):
+   station_list = []
+   for pattern in station_pattern_list :
+      parmname_list = pdb.getNames( { True : 'DirectionalGain:?:?:*:'+pattern+':*', False: 'Gain:?:?:*:' + pattern}[DirectionalGainEnable] )
+      station_list.extend(sorted(set([n.split(':')[{True : -2, False : -1}[DirectionalGainEnable]] for n in parmname_list])))
+   return station_list
+
+def repair_station_table(myion,globaldbpath,instrumentdb):
+    stations = ["*"]
+    myion.stations = get_station_list( instrumentdb, stations, myion.DirectionalGainEnable )
+    myion.N_stations = len(myion.stations)
+
+    antenna_table_name = os.path.join( globaldbpath, "ANTENNA")
+    if not os.path.exists(antenna_table_name) :
+        print("ANTENNA table not existing, please copy to globaldb")
+        return
+    antenna_table = pt.table(antenna_table_name) 
+    name_col = antenna_table.getcol('NAME')
+    position_col = antenna_table.getcol( 'POSITION' )
+    myion.station_positions = [position_col[name_col.index(station_name)] for station_name in myion.stations]
+    antenna_table.close()
+    station_table = myion.hdf5.createTable(myion.hdf5.root, 'stations', {'name': tables.StringCol(40), 'position':tables.Float64Col(3)})
+    row = station_table.row
+    for (station, position) in zip(myion.stations, myion.station_positions) : 
+        row['name'] = station
+        row['position'] = position
+        row.append()
+        station_table.flush()
+    myion.array_center = np.array( myion.station_positions ).mean(axis=0).tolist()
+    myion.hdf5.createArray(myion.hdf5.root, 'array_center', myion.array_center)
+    
+
+
+def repair_pointing(myion,globaldbpath):
+    field_table_name = os.path.join( globaldbpath, "FIELD" )
+    if not os.path.exists(field_table_name) :
+        print("FIELD table not existing, please copy to globaldb")
+        return
+    field_table = pt.table( field_table_name)
+    field_table = pt.table( globaldbpath + "/FIELD")
+    phase_dir_col = field_table.getcol('PHASE_DIR')
+    myion.pointing = phase_dir_col[0,0,:]
+    field_table.close()
+    myion.hdf5.createArray(myion.hdf5.root, 'pointing', myion.pointing)
+    
+
+def repair_sources(myion,globaldb,instrumentdb):
+    skydbname = globaldb + "/sky"
+    if not os.path.exists(skydbname) : 
+        print("No skydb found, copy first to globaldb")
+        return
+    skydb = lofar.parmdb.parmdb( skydbname )
+    sources = ["*"]
+    myion.sources = get_source_list( instrumentdb, sources )
+    myion.source_positions = []
+    for source in myion.sources :
+        try:
+            RA = skydb.getDefValues( 'Ra:' + source )['Ra:' + source][0][0]
+            dec = skydb.getDefValues( 'Dec:' + source )['Dec:' + source][0][0]
+        except KeyError:
+            # Source not found in skymodel parmdb, try to find components
+            RA = np.array(list(skydb.getDefValues( 'Ra:' + source + '.*' ).values())).mean()
+            dec = np.array(list(skydb.getDefValues( 'Dec:' + source + '.*' ).values())).mean()
+        myion.source_positions.append([RA, dec])
+
+def add_to_h5_func(h5file,data,name='test',dtype=None):
+    atom = tables.Atom.from_dtype(data.dtype)
+    if name in h5file.root:
+        h5file.removeNode('/'+name)
+    myarray=h5file.createCArray(h5file.root,name,atom,shape=data.shape)
+    myarray[:]=data
+    myarray.flush()
+        
+def doRepair(globaldbpath,
+             GainEnable = False, DirectionalGainEnable = False,
+             PhasorsEnable = False, RotationEnable = False, CommonRotationEnable = False,ScalarPhaseEnable = False, CommonScalarPhaseEnable = False,polarizations=[0,1],tablename='instrument-0'):
+    if not os.path.isdir(globaldbpath):
+        print("error:",globaldbpath,"does not exist")
+        return
+    if os.path.isfile(globaldbpath+'/ionmodel.hdf5'):
+        try:
+            myion=iono.IonosphericModel([globaldbpath])
+        except (RuntimeError, TypeError, NameError):
+            myion=dummyion()
+            myion.hdf5=tables.openFile(globaldbpath+'/ionmodel.hdf5','w')
+
+    else:
+        myion=dummyion()
+        myion.hdf5=tables.openFile(globaldbpath+'/ionmodel.hdf5','w')
+
+    myion.GainEnable = GainEnable
+    myion.DirectionalGainEnable = DirectionalGainEnable
+    myion.PhasorsEnable =PhasorsEnable 
+    myion.RotationEnable =RotationEnable 
+    myion.CommonRotationEnable =CommonRotationEnable 
+    myion.ScalarPhaseEnable =ScalarPhaseEnable 
+    myion.CommonScalarPhaseEnable =CommonScalarPhaseEnable 
+    myion.polarizations = polarizations
+    myion.N_pol = len(polarizations)
+    myion.instrument_db_list=[globaldbpath+'/%s-%i'%(tablename,idx) for idx in range(500) if os.path.isdir(globaldbpath+'/%s-%i'%(tablename,idx))]
+    
+    instrumentdb=lofar.parmdb.parmdb(myion.instrument_db_list[0])
+
+    if hasattr(myion,'stations'):
+        stations=myion.stations
+    else:
+        repair_station_table(myion,globaldbpath,instrumentdb)
+
+
+    if not hasattr(myion,'pointing'):
+        repair_pointing(myion,globaldbpath)
+
+
+    if not hasattr(myion,'sources'):
+        if DirectionalGainEnable or myion.RotationEnable or ScalarPhaseEnable:
+            print("getting source names from instrumentdb")
+            repair_sources(myion,globaldbpath,instrumentdb)
+        
+        else:
+            myion.sources = ["Pointing"]
+            myion.source_positions = [list(myion.pointing)]
+        
+        source_table = myion.hdf5.createTable(myion.hdf5.root, 'sources', {'name': tables.StringCol(40), 'position':tables.Float64Col(2)})
+        row = source_table.row
+        for (source, position) in zip(myion.sources, myion.source_positions) :
+            row['name'] = source
+            row['position'] = position
+            row.append()
+        source_table.flush()
+    myion.N_sources = len(myion.sources)
+           
+    # First collect all frequencies 
+    # We need them beforehand to sort the frequencies (frequencies are not necessarily given in sorted order)
+    if PhasorsEnable:
+        infix = ('Ampl', 'Phase')
+    else:
+        infix = ('Real', 'Imag')
+    if myion.GainEnable :
+        parmname0 = ':'.join(['Gain', str(myion.polarizations[0]), str(myion.polarizations[0]), infix[1], myion.stations[0]])
+    else:
+        if myion.DirectionalGainEnable :
+            parmname0 = ':'.join(['DirectionalGain', str(myion.polarizations[0]), str(myion.polarizations[0]), infix[1], myion.stations[0], myion.sources[0]])
+        else:
+            if myion.ScalarPhaseEnable :
+                parmname0 = ':'.join(['ScalarPhase', myion.stations[0], myion.sources[0]])
+            else:
+                if myion.CommonScalarPhaseEnable:
+                    parmname0 = ':'.join(['CommonScalarPhase', myion.stations[0]])
+    
+    parmname_check=parmname0
+    myion.freqs = []
+    myion.freqwidths = []
+    newdblist=[]
+    for instrumentdb_name in myion.instrument_db_list:
+        print("opening",instrumentdb_name,parmname_check)
+        try:
+            instrumentdb = lofar.parmdb.parmdb( instrumentdb_name )
+            v0 = instrumentdb.getValuesGrid( parmname_check )[ parmname_check ]
+            freqs = v0['freqs']
+        except:
+            print("Error opening " + instrumentdb_name,"removing from list")
+        else:
+            myion.freqs = np.concatenate([myion.freqs, freqs])
+            myion.freqwidths = np.concatenate([myion.freqwidths, v0['freqwidths']])
+            newdblist.append(instrumentdb_name)
+    myion.instrument_db_list=newdblist
+    # Sort frequencies, find both the forward and inverse mapping
+    # Mappings are such that
+    #    sorted_freqs = unsorted_freqs[sorted_freq_idx]
+    #    sorted_freqs[inverse_sorted_freq_idx] = unsorted_freqs
+    # We will use the following form
+    #    sorted_freqs[inverse_sorted_freq_idx[selection]] = unsorted_freqs[selection]
+    # to process chunks (=selections) of unsorted data and store them in sorted order
+    sorted_freq_idx = sorted(list(range(len(myion.freqs))), key = lambda idx: myion.freqs[idx])
+    inverse_sorted_freq_idx = sorted(list(range(len(myion.freqs))), key = lambda idx: sorted_freq_idx[idx])
+      
+    myion.freqs = myion.freqs[sorted_freq_idx]
+    myion.freqwidths = myion.freqwidths[sorted_freq_idx]
+    add_to_h5_func(myion.hdf5,np.array(myion.freqs),name='freqs',dtype=tables.Float64Atom())
+    add_to_h5_func(myion.hdf5,np.array(myion.freqwidths),name='freqwidths',dtype=tables.Float64Atom())
+    myion.N_freqs = len(myion.freqs)
+    
+    myion.times = v0['times']
+    myion.timewidths = v0['timewidths']
+    add_to_h5_func(myion.hdf5,myion.times,name='times',dtype=tables.Float64Atom())
+    add_to_h5_func(myion.hdf5,myion.timewidths,name='timewidths',dtype=tables.Float64Atom())
+
+    myion.N_times = len( myion.times )
+    add_to_h5_func(myion.hdf5,  np.array(myion.polarizations),name='polarizations',dtype=tables.Float32Atom())
+      
+
+    if GainEnable or DirectionalGainEnable:
+        if hasattr(myion,'phases'):
+            myion.hdf5.removeNode('/phases')
+        chunkshape = (1024 , 32, 1, 1, 1)
+        ph=myion.hdf5.createCArray(myion.hdf5.root, 'phases', tables.Float64Atom(), shape=(myion.N_times, myion.N_freqs, myion.N_stations, myion.N_sources, myion.N_pol), chunkshape = chunkshape)
+            
+        if hasattr(myion,'amplitudes'):
+            myion.hdf5.removeNode('/amplitudes')
+        chunkshape = (1024 , 32, 1, 1, 1)
+        amp = myion.hdf5.createCArray(myion.hdf5.root, 'amplitudes', tables.Float64Atom(), shape=(myion.N_times, myion.N_freqs, myion.N_stations, myion.N_sources, myion.N_pol), chunkshape = chunkshape) 
+        if not hasattr(myion,'flags'):   
+            myion.flags = myion.hdf5.createCArray(myion.hdf5.root, 'flags', tables.Float32Atom(), shape=(myion.N_times, myion.N_freqs))
+
+    else:
+      if ScalarPhaseEnable or CommonScalarPhaseEnable:
+        if hasattr(myion,'scalarphases'):
+            myion.hdf5.removeNode('/scalarphases')
+        chunkshape = (1024 , 32, 1, 1)
+        scalarph=myion.hdf5.createCArray(myion.hdf5.root, 'scalarphases', tables.Float64Atom(), shape=(myion.N_times, myion.N_freqs, myion.N_stations, myion.N_sources), chunkshape = chunkshape)
+            
+
+    if RotationEnable or CommonRotationEnable:
+        if not hasattr(myion,'rotation'):
+            chunkshape = (1024 , 32, 1, 1)
+            rotation = myion.hdf5.createCArray(myion.hdf5.root, 'rotation', tables.Float64Atom(), shape=ph.shape[:4], chunkshape = chunkshape)
+        else:
+            rotation =  myion.rotation
+
+    freq_idx = 0
+
+    for instrumentdb_name in myion.instrument_db_list:
+        print("processing",instrumentdb_name)
+        instrumentdb = lofar.parmdb.parmdb( instrumentdb_name )
+        v0 = instrumentdb.getValuesGrid( parmname_check )[ parmname_check ]
+        freqs = v0['freqs']
+        N_freqs = len(freqs)
+        sorted_freq_selection = inverse_sorted_freq_idx[freq_idx:freq_idx+N_freqs]
+        for station_idx,station in enumerate(list(myion.stations[:])):
+            for pol_idx,pol in enumerate(myion.polarizations[:]):
+                
+                if GainEnable:
+                    parmname0 = ':'.join(['Gain', str(pol), str(pol), infix[0], station])
+                    parmname1 = ':'.join(['Gain', str(pol), str(pol), infix[1], station])
+                    hasAmpl=False
+                    hasPhase=False
+                    #print " checking",parmname0,parmname0 in instrumentdb.getNames()
+                    if parmname0 in instrumentdb.getNames():
+                        hasAmpl=True
+                    if parmname1 in instrumentdb.getNames():
+                        hasPhase=True
+                    if PhasorsEnable:
+                      if hasPhase:
+                        gain_phase = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                        if gain_phase.shape != ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0, pol_idx].shape:
+                            print("wrong shape",gain_phase.shape,parmname1)
+                            continue;
+                        
+                        ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0, pol_idx] = gain_phase
+                      if hasAmpl:
+
+                        gain_amplitude = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                        amp[:,sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0, pol_idx] = gain_amplitude
+                    else:
+                        gain_real = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                        gain_imag = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+
+                        cdata=gain_real+1.j*gain_imag
+                        if cdata.shape != ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0, pol_idx].shape:
+                            print("wrong shape",cdata.shape,parmname1)
+                            continue;
+
+                        ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0, pol_idx] =np.angle(cdata)
+
+                        amp[:,sorted_freq_selection[0]:sorted_freq_selection[-1]+1 , station_idx, 0, pol_idx] = np.absolute(cdata)
+
+                if myion.DirectionalGainEnable:
+                    for source_idx,source in enumerate(myion.sources):
+                        parmname0 = ':'.join(['DirectionalGain', str(pol), str(pol), infix[0], station, source])
+                        parmname1 = ':'.join(['DirectionalGain', str(pol), str(pol), infix[1], station, source])
+                        hasAmpl=False
+                        hasPhase=False
+                        if parmname0 in instrumentdb.getNames():
+                            hasAmpl=True
+                        if parmname1 in instrumentdb.getNames():
+                            hasPhase=True
+                            
+                        if myion.PhasorsEnable:
+                            
+                            if hasPhase:
+                                gain_phase = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                                if gain_phase.shape != ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx, pol_idx].shape:
+                                    print("wrong shape",gain_phase.shape,parmname1)
+                                    continue;
+                        
+                                ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx, pol_idx] = gain_phase
+                            if hasAmpl:
+                                gain_amplitude = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                                amp[:,sorted_freq_selection[0]:sorted_freq_selection[-1]+1 , station_idx, source_idx, pol_idx] = gain_amplitude
+                        else:
+                            gain_real = instrumentdb.getValuesGrid( parmname0 )[ parmname0 ]['values']
+                            gain_imag = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+
+                            cdata=gain_real+1.j*gain_imag
+                            if cdata.shape != ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx, pol_idx].shape:
+                                print("wrong shape",cdata.shape,parmname1)
+                                continue;
+                        
+                            ph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx, pol_idx] =np.angle(cdata)
+
+                            amp[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx, pol_idx] = np.absolute(cdata)
+                     
+            if CommonScalarPhaseEnable:
+                parmname1 = ':'.join(['CommonScalarPhase', station])
+                gain_phase = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                if gain_phase.shape != scalarph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0].shape:
+                    print("wrong shape",gain_phase.shape,parmname1)
+                    continue;
+
+                scalarph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, 0] = gain_phase
+            if myion.ScalarPhaseEnable:
+                for source_idx,source in enumerate(myion.sources):
+                    parmname1 = ':'.join(['ScalarPhase', station,source])
+                    gain_phase = instrumentdb.getValuesGrid( parmname1 )[ parmname1 ]['values']
+                    if gain_phase.shape != scalarph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx].shape:
+                        print("wrong shape",gain_phase.shape,parmname1)
+                        continue;
+
+                    scalarph[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx] = gain_phase
+
+        if myion.CommonRotationEnable:
+            for station_idx,station in enumerate(myion.stations):
+                parmname = ':'.join(['CommonRotationAngle', station])
+                rot = instrumentdb.getValuesGrid( parmname )[ parmname ]['values']
+                rotation[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx,0] = rot
+
+        if myion.RotationEnable:
+            for station_idx,station in enumerate(myion.stations):
+                for source_idx,source in enumerate(myion.sources):
+                    parmname = ':'.join(['RotationAngle', station,source])
+                    rot = instrumentdb.getValuesGrid( parmname )[ parmname ]['values']
+                    if rot.shape != rotation[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx, source_idx].shape:
+                        print("wrong shape",rot.shape,parmname)
+                        continue;
+                    rotation[:, sorted_freq_selection[0]:sorted_freq_selection[-1]+1, station_idx,source_idx] = rot
+
+
+
+        freq_idx += N_freqs
+    myion.hdf5.close()
diff --git a/CEP/Calibration/ExpIon/src/sphere.py b/CEP/Calibration/ExpIon/src/sphere.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a26e4563a218ceafafebde50449be3362c769e1
--- /dev/null
+++ b/CEP/Calibration/ExpIon/src/sphere.py
@@ -0,0 +1,467 @@
+# -*- coding: utf-8 -*-
+###############################################################################
+
+# import Python modules
+from math import *
+
+# import 3rd patry modules
+from numpy import *
+import pyrap.measures
+import pyrap.quanta
+
+# import user modules
+from .acalc import *
+
+###############################################################################
+# be aware of the special cases -0 and .. 60 ..
+# TODO: implement special cases in C-code
+###############################################################################
+
+def radec_to_name( radec ):
+  rd = degdeg_to_hmsdms( radec )
+  name = '%02d%02d' % ( rd[ 0 ], rd[ 1 ] )
+  if ( radec[ 1 ] >= 0. ):
+    name = name + '+%02d' % ( rd[ 3 ] )
+  else:
+    name = name + '%03d' % ( rd[ 3 ] )
+  name = name + '%02d' % ( rd[ 4 ] )
+  return name
+
+###############################################################################
+
+def convert_radec_from_j2000( radec, epoch, m = 3.075, n = 1.336 ):
+  # TODO: make more accurate
+  # TODO: special cases for dec close to poles
+  [ ra, dec ] = radec
+  depoch = epoch - 2000.
+  mm = depoch * m * 15. / 3600.
+  nn = depoch * n * 15. / 3600.
+  dra = mm + nn * sin( radians( ra ) ) * tan( radians( dec ) )
+  ddec = nn * cos( radians( ra ) )
+  return [ amodulo( ra + dra, 360. ), dec + ddec ]
+
+###############################################################################
+
+def convert_b1950_to_j2000( radec ):
+# based on Lieske (1976)
+  [ ra1, dec1 ] = [ aradians( radec[ 0 ] ), aradians( radec[ 1 ] ) ]
+  xyz1 = array( [ cos( ra1 ) * cos( dec1 ), sin( ra1 ) * cos( dec1 ), sin( dec1 ) ], dtype = float64 )
+  rot = array( [ [   0.9999257079523629, - 0.0111789381377700, - 0.0048590038153592 ],
+                 [   0.0111789381264276,   0.9999375133499888, - 0.0000271625947142 ],
+                 [   0.0048590038414544, - 0.0000271579262585,   0.9999881946023742 ] ], dtype = float64 )
+  xyz2 = dot( rot, xyz1 ).tolist()
+  ra2 = amodulo( adegrees( atan2( xyz2[ 1 ], xyz2[ 0 ] ) ), 360. )
+  dec2 = adegrees( max( - 1., min( 1., asin( xyz2[ 2 ] ) ) ) )
+  return [ ra2, dec2 ]
+
+###############################################################################
+
+def convert_j2000_to_b1950( radec ):
+# based on Lieske (1976)
+  [ ra1, dec1 ] = [ aradians( radec[ 0 ] ), aradians( radec[ 1 ] ) ]
+  xyz1 = array( [ cos( ra1 ) * cos( dec1 ), sin( ra1 ) * cos( dec1 ), sin( dec1 ) ], dtype = float64 )
+  rot = array( [ [   0.9999257079523629,   0.0111789381264276,   0.0048590038414544 ],
+                 [ - 0.0111789381377700,   0.9999375133499888, - 0.0000271579262585 ],
+                 [ - 0.0048590038153592, - 0.0000271625947142,   0.9999881946023742 ] ], dtype = float64 )
+  xyz2 = dot( rot, xyz1 ).tolist()
+  ra2 = amodulo( adegrees( atan2( xyz2[ 1 ], xyz2[ 0 ] ) ), 360. )
+  dec2 = adegrees( asin( max( - 1., min( 1., xyz2[ 2 ] ) ) ) )
+  return [ ra2, dec2 ]
+
+###############################################################################
+
+def hmsdms_to_degdeg( hmsdms ):
+#  if __sphere:
+#    return _sphere.hmsdms_to_degdeg( [ float( x ) for x in hmsdms ] )
+  ra_h = amodulo( hmsdms[ 0 ], 24. )
+  ra = 15. * ( ra_h + ( hmsdms[ 1 ] / 60. ) + ( hmsdms[ 2 ] / 3600. ) )
+  dec_d = asign( hmsdms[ 3 ] ) * degrees( asin( 
+      max( - 1., min( 1., sin( radians( amodulo( fabs( hmsdms[ 3 ] ), 360. ) ) ) ) ) ) )
+  dec = dec_d + asign( dec_d ) * ( ( hmsdms[ 4 ] / 60. ) + ( hmsdms[ 5 ] / 3600. ) )
+  if ( dec > 90. ):
+    dec = 90.
+  elif ( dec < - 90. ):
+    dec = - 90.
+  return [ ra, dec ]
+
+###############################################################################
+
+def degdeg_to_hmsdms( degdeg, precision = None ):
+#  if __sphere:
+#    return _sphere.degdeg_to_hmsdms( [ float( x ) for x in degdeg ] )
+  ra_deg = amodulo( degdeg[ 0 ], 360. )
+  ra_h = floor( ra_deg / 15. )
+  ra_m = floor( 60. * ( ( ra_deg / 15. ) - ra_h ) )
+  ra_s = 3600. * ( ( ra_deg / 15. ) - ra_h - ( ra_m / 60. ) )
+  dec_deg = asign( degdeg[ 1 ] ) * degrees( asin( 
+      max( - 1., min( 1., sin( radians( amodulo( fabs( degdeg[ 1 ] ), 360. ) ) ) ) ) ) )
+  dec_d = asign( dec_deg ) * floor( abs( dec_deg ) )
+  dec_m = floor( 60. * abs( dec_deg - dec_d ) )
+  dec_s = 3600. * ( abs( dec_deg - dec_d ) - ( dec_m / 60. ) )
+  if ( precision != None ):
+    if ( len( shape( precision ) ) == 0 ):
+      prec1 = int( precision )
+      prec2 = int( precision )
+    elif ( len( precision ) == 1 ):
+      prec1 = int( precision[ 0 ] )
+      prec2 = int( precision[ 0 ] )
+    else:
+      prec1 = int( precision[ 0 ] )
+      prec2 = int( precision[ 1 ] )
+    ra_s = around( ra_s, decimals = prec1 )
+    dec_s = around( dec_s, decimals = prec2 )
+  if ( ra_s >= 60. ):
+    ra_s = ra_s - 60.
+    ra_m = ra_m + 1.
+  if ( ra_m >= 60. ):
+    ra_m = ra_m - 60.
+    ra_h = ra_h + 1.
+  if ( ra_h >= 24. ):
+    ra_h = ra_h - 24.
+  if ( dec_s >= 60. ):
+    dec_s = dec_s - 60.
+    dec_m = dec_m + 1.
+  if ( dec_m >= 60. ):
+    dec_m = dec_m - 60.
+    if ( asign( dec_deg ) > 0. ):
+      dec_d = dec_d + 1.
+      if ( dec_d == 90. ):
+        dec_s = 0.
+        dec_m = 0.
+    else:
+      dec_d = dec_d - 1.
+      if ( dec_d == - 90. ):
+        dec_s = 0.
+        dec_m = 0.
+  return [ ra_h, ra_m, ra_s, dec_d, dec_m, dec_s ]
+
+###############################################################################
+
+def degdeg_to_dmsdms( degdeg, precision = None ):
+#  if __sphere:
+#    return _sphere.degdeg_to_dmsdms( [ float( x ) for x in degdeg ] )
+  lon_deg = amodulo( degdeg[ 0 ] + 180., 360. ) - 180.
+  lon_d = asign( lon_deg ) * floor( abs( lon_deg ) )
+  lon_m = floor( 60. * abs( lon_deg - lon_d ) )
+  lon_s = 3600. * ( abs( lon_deg - lon_d ) - ( lon_m / 60. ) )
+  lat_deg = degrees( asin( 
+      max( - 1., min( 1., sin( radians( amodulo( degdeg[ 1 ], 360. ) ) ) ) ) ) )
+  lat_d = asign( lat_deg ) * floor( abs( lat_deg ) )
+  lat_m = floor( 60. * abs( lat_deg - lat_d ) )
+  lat_s = 3600. * ( abs( lat_deg - lat_d ) - ( lat_m / 60. ) )
+  if ( precision != None ):
+    if ( len( shape( precision ) ) == 0 ):
+      prec1 = int( precision )
+      prec2 = int( precision )
+    elif ( len( precision ) == 1 ):
+      prec1 = int( precision[ 0 ] )
+      prec2 = int( precision[ 0 ] )
+    else:
+      prec1 = int( precision[ 0 ] )
+      prec2 = int( precision[ 1 ] )
+    lon_s = around( lon_s, decimals = prec1 )
+    lat_s = around( lat_s, decimals = prec2 )
+  if ( lon_s >= 60. ):
+    lon_s = lon_s - 60.
+    lon_m = lon_m + 1.
+  if ( lon_m >= 60. ):
+    lon_m = lon_m - 60.
+    lon_d = lon_d + 1.
+  if ( lon_d >= 360. ):
+    lon_d = lon_d - 360.
+  if ( lat_s >= 60. ):
+    lat_s = lat_s - 60.
+    lat_m = lat_m + 1.
+  if ( lat_m >= 60. ):
+    lat_m = lat_m - 60.
+    if ( asign( lat_deg ) > 0. ):
+      lat_d = lat_d + 1.
+      if ( lat_d == 90. ):
+        lat_s = 0.
+        lat_m = 0.
+    else:
+      lat_d = dec_d - 1.
+      if ( lat_d == - 90. ):
+        lat_s = 0.
+        lat_m = 0.
+  return( [ lon_d, lon_m, lon_s, lat_d, lat_m, lat_s ] )
+
+###############################################################################
+
+def calculate_angular_separation( lonlat0, lonlat1 ):
+   me = pyrap.measures.measures()
+   lon0 = pyrap.quanta.quantity( lonlat0[ 0 ], 'rad' )
+   lat0 = pyrap.quanta.quantity( lonlat0[ 1 ], 'rad' )
+   lon1 = pyrap.quanta.quantity( lonlat1[ 0 ], 'rad' )
+   lat1 = pyrap.quanta.quantity( lonlat1[ 1 ], 'rad' )
+   direction1 = me.direction('', lon0, lat0 )
+   direction2 = me.direction('', lon1, lat1 )
+   separation = me.separation(direction1, direction2).get_value('rad')
+   angle = me.posangle(direction1, direction2).get_value('rad')
+   
+   return [ separation, angle ]
+
+###############################################################################
+
+def calculate_offset_position( degdeg, radius, angle ):
+# 0. <= radius <= 180.
+  if __sphere:
+    return _sphere.calculate_offset_position( [ float( x ) for x in degdeg ],
+        float( radius ), float( angle ) )
+  ra = degdeg[ 0 ]
+  dec = degdeg[ 1 ]
+  if ( radius <= 0. ):
+    new_ra = ra
+    new_dec = dec
+  else:
+    a = radians( radius )
+    c = radians( 90. - dec )
+    B = radians( - angle )
+    b = acos( max( - 1., min( 1., sin( a ) * cos( B ) * sin( c ) + cos( a ) * cos( c ) ) ) )
+    if ( b == 0. ):
+      A = 0.
+    else:
+      A = asin( max( - 1., min( 1., sin( a ) * sin( B ) / sin( b ) ) ) )
+      if ( ( ( cos( a ) * sin( c ) - sin( a ) * cos( B ) * cos( c ) ) / sin( b ) ) < 0. ):
+        A = pi - A
+    new_ra = amodulo( ra - degrees( A ), 360. )
+    new_dec = 90. - degrees( b )
+  return [ new_ra, new_dec ]
+
+###############################################################################
+
+def xyz_to_llr( xyz ):
+  if __sphere:
+    return _sphere.xyz_to_llr( [ float( x ) for x in xyz ] )
+  x = xyz[ 0 ]
+  y = xyz[ 1 ]
+  z = xyz[ 2 ]
+  lon = amodulo( degrees( atan2( y, x ) ) + 180., 360. ) - 180.
+  lat = degrees( atan2( z, sqrt( x**2 + y**2 ) ) )
+  rad = sqrt( x**2 + y**2 + z**2 )
+  return [ lon, lat, rad ]
+
+###############################################################################
+
+def xyz_to_geo_llh( xyz, time ):
+# default Earth ellipticity definition (a,f) is WGS (1984)
+# Note that longitude is defined as positive towards east, just like RA
+
+   [ x, y, z ] = xyz
+   me = pyrap.measures.measures()
+   x = pyrap.quanta.quantity(x, 'm')
+   y = pyrap.quanta.quantity(y, 'm')
+   z = pyrap.quanta.quantity(z, 'm')
+   pos_itrf =  me.position( 'itrf', x, y, z )
+   
+   t = pyrap.quanta.quantity(time, 's')
+   t1 = me.epoch('utc', t)
+   me.doframe(t1)
+   
+   pos_wgs84 = me.measure(pos_itrf, 'wgs84')
+   glon = pos_wgs84['m0']['value']
+   glat = pos_wgs84['m1']['value']
+   gh = pos_wgs84['m2']['value']
+
+  #[ x, y, z ] = xyz
+  #glon = atan2( y, x )
+  #glat = atan2( z, sqrt( x**2 + y**2 ) )
+  #gh = sqrt( x**2 + y**2 + z**2 ) - a * sqrt( 1. - f )
+  #if ( iterations > 0 ):
+    #phi = glat
+    #for i in range( iterations ):
+      #n = a / sqrt( 1. - e2 * ( sin( phi )**2 ) )
+      #gh = ( sqrt( x**2 + y**2 ) / cos( phi ) ) - n
+      #phi = atan( z / ( sqrt( x**2 + y**2 ) * ( 1. - e2 * ( n / ( n + gh ) ) ) ) )
+    #glat = phi
+
+   return [ glon, glat, gh ]
+
+###############################################################################
+
+def geo_llh_to_xyz( geo_llh, a = 6378137., f = 1. / 298.257, e2 = 6.6943799013e-3 ):
+# default Earth ellipticity definition (a,f) is WGS (1984)
+# Note that longitude is defined as positive towards east, just like RA
+  if __sphere:
+    return _sphere.geo_llh_to_xyz( [ float( x ) for x in geo_llh ], float( a ),
+        float( f ), float( e2 ) )
+  [ glon, glat, gh ] = geo_llh
+  lamda = radians( glon )
+  phi = radians( glat )
+  n = a / sqrt( 1. - e2 * ( sin( phi )**2 ) )
+  x = ( n + gh ) * cos( phi ) * cos( lamda )
+  y = ( n + gh ) * cos( phi ) * sin( lamda )
+  z = ( n * ( 1. - e2 ) + gh ) * sin( phi )
+  return [ x, y, z ]
+
+###############################################################################
+
+def calculate_hour_angles_at_elevation_limit( lat, dec, elevation_limit = 0. ):
+  if __sphere:
+    return _sphere.calculate_hour_angles_at_elevation_limit( float( lat ), float( dec ),
+        float( elevation_limit ) )
+  if ( ( dec + lat >= 90. ) or ( dec + lat <= - 90. ) ): # check for circumpolar sources
+    ha = 180.
+  elif ( ( dec - lat >= 90. ) or ( dec - lat <= - 90. ) ): # check for non-visible sources
+    ha = 0.
+  else:
+    a = radians( 90. - elevation_limit )
+    b = radians( 90. - dec ) # 0 < b < 180
+    c = radians( 90. - lat ) # 0 < c < 180
+    A = acos( max( - 1., min( 1., ( cos( a ) - cos( b ) * cos( c ) ) / (  sin( b ) * sin( c ) ) ) ) )
+    # 0 < A < 180 degrees
+    ha = degrees( A )
+  return [ - ha, ha ]
+
+###############################################################################
+
+def time_to_dhms( time ):
+  if __sphere:
+    return _sphere.time_to_dhms( float( time ) )
+  res = abs( time )
+  day = sign( time ) * floor( res )
+  res = 24. ( res - day )
+  hour = floor( res )
+  res = 60. * ( res - hour )
+  mins = floor( res )
+  sec = 60. * ( res - mins )
+  return [ day, hour, mins, sec ]
+
+###############################################################################
+
+def dhms_to_time( dhms ):
+#  if __sphere:
+#    return _sphere.dhms_to_time( [ float( x ) for x in dhms ] )
+  [ day, hour, mins, sec ] = dhms
+  time = float( day ) + ( float( hour ) / 24. ) + ( float( mins ) / 1440. ) + ( float( sec ) / 86400. )
+  return time
+
+###############################################################################
+
+def calculate_enu( ref_xyz, xyz ):
+  rot_xyz = array( xyz, dtype = float64 )
+  ref_geo_llh = xyz_to_geo_llh( ref_xyz )
+  ref_lon = radians( ref_geo_llh[ 0 ] )
+  ref_lat = radians( ref_geo_llh[ 1 ] )
+  rot = array( [ [ - sin( ref_lon )                 ,   cos( ref_lon )                 ,             0. ], 
+                 [ - cos( ref_lon ) * sin( ref_lat ), - sin( ref_lon ) * sin( ref_lat ), cos( ref_lat ) ],
+                 [   cos( ref_lon ) * cos( ref_lat ),   sin( ref_lon ) * cos( ref_lat ), sin( ref_lat ) ] ],
+                 dtype = float64 )
+  rot_xyz = dot( rot, rot_xyz )
+  return rot_xyz.tolist()
+
+###############################################################################
+
+def calculate_local_sky_position( geo_xyz, radec, time ):
+   me = pyrap.measures.measures()
+   x = pyrap.quanta.quantity(geo_xyz[0], 'm')
+   y = pyrap.quanta.quantity(geo_xyz[1], 'm')
+   z = pyrap.quanta.quantity(geo_xyz[2], 'm')
+   position =  me.position( 'itrf', x, y, z )
+   me.doframe( position )
+   RA = pyrap.quanta.quantity( radec[0], 'rad' )
+   dec = pyrap.quanta.quantity( radec[1], 'rad' )
+   direction =  me.direction( 'j2000', RA, dec )
+   t = pyrap.quanta.quantity(time, 's')
+   t1 = me.epoch('utc', t)
+   me.doframe(t1)
+   a = me.measure(direction, 'azelgeo')
+   azimuth = a['m0']['value']
+   elevation = a['m1']['value']
+   zenith_angle = pi/2 - elevation
+
+   return [ zenith_angle, azimuth ]
+
+###############################################################################
+
+def calculate_puncture_point( xyz, radec, time, height = 400.e3, iterations = 4 ):
+# height in meters
+# radec at J2000
+
+   # initialize some variables
+   ant_xyz = array( xyz, dtype = float64 )
+   ant_geo_llh = xyz_to_geo_llh( xyz )
+   ant_lon = ant_geo_llh[ 0 ]
+   ant_lat = ant_geo_llh[ 1 ]
+   ant_lh = ant_geo_llh[ 2 ]
+   if ( ant_lh > height ):
+      raise error( 'specified location has a height larger than the puncture layer' )
+   rot = array( [ [ - sin( ant_lon )                 ,   cos( ant_lon )                 ,             0. ], 
+                  [ - cos( ant_lon ) * sin( ant_lat ), - sin( ant_lon ) * sin( ant_lat ), cos( ant_lat ) ],
+                  [   cos( ant_lon ) * cos( ant_lat ),   sin( ant_lon ) * cos( ant_lat ), sin( ant_lat ) ] ],
+                  dtype = float64 )
+   ant_za_az = calculate_local_sky_position( ant_xyz, radec, time )
+   ant_za = ant_za_az[ 0 ]
+   ant_az = ant_za_az[ 1 ]
+   len2_ant_xyz = ( ant_xyz**2 ).sum()
+   local_src_dxyz = array( [ sin( ant_za ) * sin( ant_az ), sin( ant_za ) * cos( ant_az ), cos( ant_za ) ],
+       dtype = float64 )
+   src_dxyz = dot( local_src_dxyz, rot )
+
+   # determine xyz coordinates of puncture point through vector algebra
+   B = 2. * ( ant_xyz * src_dxyz ).sum()
+   len2_pp_xyz = ( sqrt( len2_ant_xyz ) + ( height - ant_lh ) )**2
+   for i in range( iterations ):
+      C = len2_ant_xyz - len2_pp_xyz # always < 0
+      len_src_xyz = ( sqrt( B**2 - 4. * C ) - B ) / 2. # always > 0
+      src_xyz = len_src_xyz * src_dxyz
+      pp_xyz = ant_xyz + src_xyz
+      len_pp_xyz = sqrt( ( pp_xyz**2 ).sum() )
+      pp_geo_llh = xyz_to_geo_llh( pp_xyz.tolist() )
+      dlen_pp_xyz = height - pp_geo_llh[ 2 ]
+      len2_pp_xyz = ( len_pp_xyz + dlen_pp_xyz )**2
+   C = len2_ant_xyz - len2_pp_xyz # always < 0
+   len_src_xyz = ( sqrt( B**2 - 4. * C ) - B ) / 2. # always > 0
+   src_xyz = len_src_xyz * src_dxyz
+   pp_xyz = ant_xyz + src_xyz
+
+   # determine zenith angle at puncture point
+   pp_geo_llh = xyz_to_geo_llh( pp_xyz.tolist() )
+   [ separation, angle ] = calculate_angular_separation( ant_geo_llh[ 0 : 2 ], pp_geo_llh[ 0 : 2 ] )
+   pp_za = ant_za_az[ 0 ] - separation
+
+   return [ pp_xyz.tolist(), float( pp_za ) ]
+
+###############################################################################
+def calculate_puncture_point_mevius( xyz, radec, time, height = 400.e3):
+# height in meters
+# radec at J2000
+
+   # initialize some variables
+   ant_xyz = array( xyz, dtype = float64 )
+   ant_geo_llh = xyz_to_geo_llh( xyz, time )
+   ant_lon = ant_geo_llh[ 0 ]
+   ant_lat = ant_geo_llh[ 1 ]
+   ant_lh = ant_geo_llh[ 2 ]
+   if ( ant_lh > height ):
+      raise error( 'specified location has a height larger than the puncture layer' )
+
+   rot = array( [ [ - sin( ant_lon )                 ,   cos( ant_lon )                 ,             0. ], 
+                  [ - cos( ant_lon ) * sin( ant_lat ), - sin( ant_lon ) * sin( ant_lat ), cos( ant_lat ) ],
+                  [   cos( ant_lon ) * cos( ant_lat ),   sin( ant_lon ) * cos( ant_lat ), sin( ant_lat ) ] ],
+                  dtype = float64 )
+   ant_za_az = calculate_local_sky_position( ant_xyz, radec, time )
+   ant_za = ant_za_az[ 0 ]
+   ant_az = ant_za_az[ 1 ]
+   len_ant_xyz = sqrt(( ant_xyz**2 ).sum())
+   
+   # This expression gives some sort of local earth radius, but the result is 
+   # inconisistent with the local curvature of the earth
+   R_earth = len_ant_xyz - ant_lh
+   
+   R_pp = R_earth + height
+   
+   pp_za = arcsin(sin(ant_za)*len_ant_xyz / R_pp)
+
+   len_src_xyz = R_pp*sin(ant_za - pp_za)/sin(ant_za)
+   
+   local_src_dxyz = array( [ sin( ant_za ) * sin( ant_az ), sin( ant_za ) * cos( ant_az ), cos( ant_za ) ],
+       dtype = float64 )
+   src_dxyz = dot( local_src_dxyz, rot )
+   src_xyz = len_src_xyz * src_dxyz
+   pp_xyz = ant_xyz + src_xyz
+
+   return [ pp_xyz.tolist(), float( pp_za ) ]
+
+###############################################################################
+
diff --git a/CEP/Calibration/StationResponse/CMakeLists.txt b/CEP/Calibration/StationResponse/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..16350a206a86c1e877f491e6a2e865ddebdf9386
--- /dev/null
+++ b/CEP/Calibration/StationResponse/CMakeLists.txt
@@ -0,0 +1,14 @@
+# $Id$
+
+lofar_package(StationResponse 0.1 DEPENDS Common ElementResponse)
+
+include(LofarFindPackage)
+lofar_find_package(Boost REQUIRED)
+lofar_find_package(Casacore REQUIRED COMPONENTS casa measures ms tables images coordinates)
+
+# Uncomment to check for unsafe conversions (gcc), for example conversion of
+# size_t to unsigned int (truncation).
+#add_definitions(-Wconversion)
+
+add_subdirectory(include/StationResponse)
+add_subdirectory(src)
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/AntennaField.h b/CEP/Calibration/StationResponse/include/StationResponse/AntennaField.h
new file mode 100644
index 0000000000000000000000000000000000000000..934e0b2247251b8628449391c62277ffb526a319
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/AntennaField.h
@@ -0,0 +1,261 @@
+//# AntennaField.h: Representation of a LOFAR antenna field, with methods to
+//# compute its response to incoming radiation.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_ARRAYFIELD_H
+#define LOFAR_STATIONRESPONSE_ARRAYFIELD_H
+
+// \file
+// Representation of a LOFAR antenna field, with methods to compute its response
+// to incoming radiation.
+
+#include <Common/lofar_smartptr.h>
+#include <Common/lofar_string.h>
+#include <Common/lofar_vector.h>
+#include <StationResponse/Constants.h>
+#include <StationResponse/Types.h>
+#include <StationResponse/ITRFDirection.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+/**
+ *  \brief Base class that represents a generic LOFAR antenna field.
+ */
+class AntennaField
+{
+public:
+    typedef shared_ptr<AntennaField>        Ptr;
+    typedef shared_ptr<const AntennaField>  ConstPtr;
+
+    /**
+     *  \brief Antenna field coordinate system.
+     *
+     *  A right handed, cartesian, local coordinate system with coordinate axes
+     *  \p p, \p q, and \p r is associated with each antenna field.
+     *
+     *  The r-axis is orthogonal to the antenna field, and points towards the
+     *  local pseudo zenith.
+     *
+     *  The q-axis is the northern bisector of the \p X and \p Y dipoles, i.e.
+     *  it is the reference direction from which the orientation of the dual
+     *  dipole antennae is determined. The q-axis points towards the North at
+     *  the core. At remote sites it is defined as the intersection of the
+     *  antenna field plane and a plane parallel to the meridian plane at the
+     *  core. This ensures the reference directions at all sites are similar.
+     *
+     *  The p-axis is orthogonal to both other axes, and points towards the East
+     *  at the core.
+     *
+     *  The axes and origin of the anntena field coordinate system are expressed
+     *  as vectors in the geocentric, cartesian, ITRF coordinate system, in
+     *  meters.
+     *
+     *  \sa "LOFAR Reference Plane and Reference Direction", M.A. Brentjens,
+     *  LOFAR-ASTRON-MEM-248.
+     */
+    struct CoordinateSystem
+    {
+        struct Axes
+        {
+            vector3r_t  p;
+            vector3r_t  q;
+            vector3r_t  r;
+        };
+
+        vector3r_t  origin;
+        Axes        axes;
+    };
+
+    /** A single antenna. */
+    struct Antenna
+    {
+        /**
+         *  \brief Position of the antenna relative to the antenna field center
+         *  (origin). This is a vector expressed in the geocentric, cartesian,
+         *  ITRF coordinate system, in meters.
+         */
+        vector3r_t  position;
+
+        /**
+         *  \brief Status of the \p X and \p Y signal paths of the antenna,
+         *  respectively.
+         */
+        bool        enabled[2];
+    };
+
+    typedef vector<Antenna> AntennaList;
+
+
+    AntennaField(const string &name, const CoordinateSystem &coordinates);
+
+    virtual ~AntennaField();
+
+    /** Return the name of the antenna field. */
+    const string &name() const;
+
+    /** Return the phase reference position of the antenna field. */
+    const vector3r_t &position() const;
+
+    /** Return the antenna field coordinate system. */
+    const CoordinateSystem &coordinates() const;
+
+    /** Add an antenna to the antenna field. */
+    void addAntenna(const Antenna &antenna);
+
+    /** Return the number of antennae in the antenna field. */
+    size_t nAntennae() const;
+
+    /*!
+     *  \name Antennae accessors
+     *  These member functions provide access to the antennae that are part of
+     *  the antenna field.
+     */
+    // @{
+
+    /** Return a read-only reference to the antenna with the requested index. */
+    const Antenna &antenna(size_t n) const;
+
+    /** Return a writeable reference to the antenna with the requested index. */
+    Antenna &antenna(size_t n);
+
+    /** Return a read-only iterator that points to the first antenna of the
+     *  antenna field.
+     */
+    AntennaList::const_iterator beginAntennae() const;
+
+    /** Return a read-only iterator that points one position past the last
+     *  antenna of the antenna field.
+     */
+    AntennaList::const_iterator endAntennae() const;
+
+    // @}
+
+    /*!
+     *  \brief Compute the response of the antenna field for a plane wave of
+     *  frequency \p freq, arriving from direction \p direction, with the analog
+     *  %tile beam former steered towards \p direction0. For LBA antenna fields,
+     *  \p direction0 has no effect.
+     *
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Frequency of the plane wave (Hz).
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param direction0 Tile beam former reference direction (ITRF, m).
+     *  \return Jones matrix that represents the response of the antenna field.
+     *
+     *  The directions \p direction, and \p direction0 are vectors that
+     *  represent a direction of \e arrival. These vectors have unit length and
+     *  point \e from the ground \e towards the direction from which the plane
+     *  wave arrives.
+     */
+    virtual matrix22c_t response(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    /*!
+     *  \brief Compute the array factor of the antenna field for a plane wave of
+     *  frequency \p freq, arriving from direction \p direction, analog %tile
+     *  beam former steered towards \p direction0. For LBA antenna fields,
+     *  \p direction0 has no effect.
+     *
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Frequency of the plane wave (Hz).
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param direction0 Tile beam former reference direction (ITRF, m).
+     *  \return A diagonal matrix with the array factor of the X and Y antennae.
+     *
+     *  The directions \p direction, and \p direction0 are vectors that
+     *  represent a direction of \e arrival. These vectors have unit length and
+     *  point \e from the ground \e towards the direction from which the plane
+     *  wave arrives.
+     */
+    virtual diag22c_t arrayFactor(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    /*!
+     *  \brief Compute the response of the antenna field for a plane wave of
+     *  frequency \p freq, arriving from direction \p direction, with the analog
+     *  %tile beam former steered towards \p direction0. For LBA antenna fields,
+     *  \p direction0 has no effect.
+     *
+     *  This method returns the non-normalized (raw) response. This allows the
+     *  response of several antenna fields to be summed together, followed by
+     *  normalization of the sum.
+     *
+     *  \see response(real_t time, real_t freq, const vector3r_t &direction,
+     *  const vector3r_t &direction0) const
+     */
+    virtual raw_response_t rawResponse(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    /*!
+     *  \brief Compute the array factor of the antenna field for a plane wave of
+     *  frequency \p freq, arriving from direction \p direction, analog %tile
+     *  beam former steered towards \p direction0. For LBA antenna fields,
+     *  \p direction0 has no effect.
+     *
+     *  This method returns the non-normalized (raw) array factor. This allows
+     *  the array factor of several antenna fields to be summed together,
+     *  followed by normalization of the sum.
+     *
+     *  \see diag22c_t arrayFactor(real_t time, real_t freq,
+     *  const vector3r_t &direction, const vector3r_t &direction0) const
+     */
+    virtual raw_array_factor_t rawArrayFactor(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const = 0;
+
+    /*!
+     *  \brief Compute the response of a single antenna for a plane wave of
+     *  frequency \p freq, arriving from direction \p direction.
+     *
+     */
+    virtual matrix22c_t elementResponse(real_t time, real_t freq,
+        const vector3r_t &direction) const = 0;
+
+protected:
+    /** Compute the parallactic rotation. */
+    matrix22r_t rotation(real_t time, const vector3r_t &direction) const;
+
+    /** Transform a vector from ITRF to antenna field coordinates. */
+    vector3r_t itrf2field(const vector3r_t &itrf) const;
+
+private:
+    vector3r_t ncp(real_t time) const;
+
+    string              itsName;
+    CoordinateSystem    itsCoordinateSystem;
+    AntennaList         itsAntennae;
+    ITRFDirection::Ptr  itsNCP;
+    mutable real_t      itsNCPCacheTime;
+    mutable vector3r_t  itsNCPCacheDirection;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldHBA.h b/CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldHBA.h
new file mode 100644
index 0000000000000000000000000000000000000000..6caafee496441b05685dafa17c97a54e51bdd7a4
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldHBA.h
@@ -0,0 +1,73 @@
+//# AntennaFieldHBA.h: Representation of an HBA antenna field.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_ANTENNAFIELDHBA_H
+#define LOFAR_STATIONRESPONSE_ANTENNAFIELDHBA_H
+
+// \file
+// Representation of an HBA antenna field.
+
+#include <StationResponse/AntennaField.h>
+#include <StationResponse/AntennaModelHBA.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class AntennaFieldHBA: public AntennaField
+{
+public:
+    typedef shared_ptr<AntennaFieldHBA>         Ptr;
+    typedef shared_ptr<const AntennaFieldHBA>   ConstPtr;
+
+    AntennaFieldHBA(const string &name, const CoordinateSystem &coordinates,
+        const AntennaModelHBA::ConstPtr &model);
+
+    virtual matrix22c_t response(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    virtual diag22c_t arrayFactor(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    virtual raw_response_t rawResponse(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    virtual raw_array_factor_t rawArrayFactor(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    virtual matrix22c_t elementResponse(real_t time, real_t freq,
+        const vector3r_t &direction) const;
+
+private:
+    AntennaModelHBA::ConstPtr   itsAntennaModel;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldLBA.h b/CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldLBA.h
new file mode 100644
index 0000000000000000000000000000000000000000..f4c082542910e7bf94109bed6ab3ff3c828d8c94
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldLBA.h
@@ -0,0 +1,64 @@
+//# AntennaFieldLBA.h: Representation of an LBA antenna field.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_ANTENNAFIELDLBA_H
+#define LOFAR_STATIONRESPONSE_ANTENNAFIELDLBA_H
+
+// \file
+// Representation of an LBA antenna field.
+
+#include <StationResponse/AntennaField.h>
+#include <StationResponse/AntennaModelLBA.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class AntennaFieldLBA: public AntennaField
+{
+public:
+    typedef shared_ptr<AntennaFieldLBA>         Ptr;
+    typedef shared_ptr<const AntennaFieldLBA>   ConstPtr;
+
+    AntennaFieldLBA(const string &name, const CoordinateSystem &coordinates,
+        const AntennaModelLBA::ConstPtr &model);
+
+    virtual raw_array_factor_t rawArrayFactor(real_t time, real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    virtual matrix22c_t elementResponse(real_t time, real_t freq,
+        const vector3r_t &direction) const;
+
+private:
+    AntennaModelLBA::ConstPtr   itsAntennaModel;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/AntennaModelHBA.h b/CEP/Calibration/StationResponse/include/StationResponse/AntennaModelHBA.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ef452c0d5be39b56da9b69004a737018dfdca8d
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/AntennaModelHBA.h
@@ -0,0 +1,69 @@
+//# AntennaModelHBA.h: HBA antenna model interface definitions.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_ANTENNAMODELHBA_H
+#define LOFAR_STATIONRESPONSE_ANTENNAMODELHBA_H
+
+// \file
+// HBA antenna model interface definitions.
+
+#include <StationResponse/Types.h>
+#include <Common/lofar_smartptr.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class AntennaModelHBA
+{
+public:
+    typedef shared_ptr<AntennaModelHBA>         Ptr;
+    typedef shared_ptr<const AntennaModelHBA>   ConstPtr;
+
+    virtual ~AntennaModelHBA();
+
+    virtual matrix22c_t response(real_t freq, const vector3r_t &direction,
+        const vector3r_t &direction0) const;
+
+    virtual diag22c_t arrayFactor(real_t freq, const vector3r_t &direction,
+        const vector3r_t &direction0) const;
+
+    virtual raw_response_t rawResponse(real_t freq, const vector3r_t &direction,
+        const vector3r_t &direction0) const;
+
+    virtual raw_array_factor_t rawArrayFactor(real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const = 0;
+
+    virtual matrix22c_t elementResponse(real_t freq,
+        const vector3r_t &direction) const = 0;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/AntennaModelLBA.h b/CEP/Calibration/StationResponse/include/StationResponse/AntennaModelLBA.h
new file mode 100644
index 0000000000000000000000000000000000000000..7b6bfa543c633284dd52813866bbe6fa9c2ac9d4
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/AntennaModelLBA.h
@@ -0,0 +1,57 @@
+//# AntennaModelLBA.h: LBA antenna model interface definitions.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_ANTENNAMODELLBA_H
+#define LOFAR_STATIONRESPONSE_ANTENNAMODELLBA_H
+
+// \file
+// LBA antenna model interface definitions.
+
+#include <StationResponse/Types.h>
+#include <Common/lofar_smartptr.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class AntennaModelLBA
+{
+public:
+    typedef shared_ptr<AntennaModelLBA>         Ptr;
+    typedef shared_ptr<const AntennaModelLBA>   ConstPtr;
+
+    virtual ~AntennaModelLBA();
+
+    virtual matrix22c_t response(real_t freq, const vector3r_t &direction)
+        const = 0;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/CMakeLists.txt b/CEP/Calibration/StationResponse/include/StationResponse/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b4dc8dff3c3c708ec21a53a78bc8c087148742f7
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/CMakeLists.txt
@@ -0,0 +1,23 @@
+# $Id$
+
+# Create symbolic link to include directory.
+execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_BINARY_DIR}/include/${PACKAGE_NAME})
+
+# Install header files.
+install(FILES
+  AntennaField.h
+  AntennaFieldHBA.h
+  AntennaFieldLBA.h
+  AntennaModelHBA.h
+  AntennaModelLBA.h
+  Constants.h
+  DualDipoleAntenna.h
+  ITRFDirection.h
+  LofarMetaDataUtil.h
+  MathUtil.h
+  Station.h
+  TileAntenna.h
+  Types.h
+  DESTINATION include/${PACKAGE_NAME})
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/Constants.h b/CEP/Calibration/StationResponse/include/StationResponse/Constants.h
new file mode 100644
index 0000000000000000000000000000000000000000..c77be6d22cdbe63369c4f9cc3a6c9d45745d62a4
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/Constants.h
@@ -0,0 +1,60 @@
+//# Constants.h: %Constants used in this library.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_CONSTANTS_H
+#define LOFAR_STATIONRESPONSE_CONSTANTS_H
+
+// \file
+// %Constants used in this library.
+
+#include <StationResponse/Types.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+/** %Constants used in this library. */
+namespace Constants
+{
+/** 2.0 * pi */
+const real_t _2pi = 6.283185307179586476925286;
+
+/** pi / 2.0 */
+const real_t pi_2 = 1.570796326794896619231322;
+
+/** pi / 4.0 */
+const real_t pi_4 = 0.7853981633974483096156608;
+
+/** Speed of light (m/s) */
+const real_t c = 2.99792458e+08;
+} //# namespace Constants
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/DualDipoleAntenna.h b/CEP/Calibration/StationResponse/include/StationResponse/DualDipoleAntenna.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ac6439fc6e7572464a4f29cb72362b2d3d5df96
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/DualDipoleAntenna.h
@@ -0,0 +1,55 @@
+//# DualDipoleAntenna.h: Semi-analytical model of a LOFAR LBA dual dipole
+//# antenna.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_DUALDIPOLEANTENNA_H
+#define LOFAR_STATIONRESPONSE_DUALDIPOLEANTENNA_H
+
+// \file
+// Semi-analytical model of a LOFAR LBA dual dipole antenna.
+
+#include <StationResponse/AntennaModelLBA.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class DualDipoleAntenna: public AntennaModelLBA
+{
+public:
+    typedef shared_ptr<DualDipoleAntenna>       Ptr;
+    typedef shared_ptr<const DualDipoleAntenna> ConstPtr;
+
+    virtual matrix22c_t response(real_t freq, const vector3r_t &direction)
+        const;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/ITRFDirection.h b/CEP/Calibration/StationResponse/include/StationResponse/ITRFDirection.h
new file mode 100644
index 0000000000000000000000000000000000000000..280a6111a99443ddbe180c8eb45bdd5a72906d45
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/ITRFDirection.h
@@ -0,0 +1,65 @@
+//# ITRFDirection.h: Functor that maps time to an ITRF direction.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_ITRFDIRECTION_H
+#define LOFAR_STATIONRESPONSE_ITRFDIRECTION_H
+
+// \file
+// Functor that maps time to an ITRF direction.
+
+#include <StationResponse/Types.h>
+#include <Common/lofar_smartptr.h>
+
+#include <casacore/measures/Measures/MeasFrame.h>
+#include <casacore/measures/Measures/MeasConvert.h>
+#include <casacore/measures/Measures/MCDirection.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class ITRFDirection
+{
+public:
+    typedef shared_ptr<ITRFDirection>       Ptr;
+    typedef shared_ptr<const ITRFDirection> ConstPtr;
+
+    ITRFDirection(const vector3r_t &position, const vector2r_t &direction);
+    ITRFDirection(const vector3r_t &position, const vector3r_t &direction);
+
+    vector3r_t at(real_t time) const;
+
+private:
+    mutable casacore::MeasFrame             itsFrame;
+    mutable casacore::MDirection::Convert   itsConverter;
+};
+
+// @}
+
+} //# namespace BBS
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/LofarMetaDataUtil.h b/CEP/Calibration/StationResponse/include/StationResponse/LofarMetaDataUtil.h
new file mode 100644
index 0000000000000000000000000000000000000000..57327b0ed0eb3c0bfbdd7857ef71b4335822a401
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/LofarMetaDataUtil.h
@@ -0,0 +1,65 @@
+//# LofarMetaDataUtil.h: Utility functions to read the meta data relevant for
+//# simulating the beam from LOFAR observations stored in MS format.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_LOFARMETADATAUTIL_H
+#define LOFAR_STATIONRESPONSE_LOFARMETADATAUTIL_H
+
+// \file
+// Utility functions to read the meta data relevant for simulating the beam from
+// LOFAR observations stored in MS format.
+
+#include <StationResponse/Station.h>
+#include <casacore/ms/MeasurementSets/MeasurementSet.h>
+#include <casacore/ms/MeasurementSets/MSAntennaColumns.h>
+#include <casacore/measures/Measures/MDirection.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+Station::Ptr readStation(const casacore::MeasurementSet &ms, unsigned int id);
+
+template <typename T>
+void readStations(const casacore::MeasurementSet &ms, T out_it)
+{
+    casacore::ROMSAntennaColumns antenna(ms.antenna());
+    for(unsigned int i = 0; i < antenna.nrow(); ++i)
+    {
+        *out_it++ = readStation(ms, i);
+    }
+}
+
+// Read the tile beam direction from a LOFAR MS. If it is not defined,
+// this function returns the delay center.
+casacore::MDirection readTileBeamDirection(const casacore::MeasurementSet &ms);
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/MathUtil.h b/CEP/Calibration/StationResponse/include/StationResponse/MathUtil.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb16fb898c2e0f9e229b7253823d53525fcb502a
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/MathUtil.h
@@ -0,0 +1,172 @@
+//# MathUtil.h: Various mathematical operations on vectors and matrices.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_MATHUTIL_H
+#define LOFAR_STATIONRESPONSE_MATHUTIL_H
+
+// \file
+// Various mathematical operations on vectors and matrices.
+
+#include <StationResponse/Constants.h>
+#include <StationResponse/Types.h>
+#include <Common/lofar_math.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+inline real_t dot(const vector3r_t &arg0, const vector3r_t &arg1)
+{
+    return arg0[0] * arg1[0] + arg0[1] * arg1[1] +  arg0[2] * arg1[2];
+}
+
+inline double norm(const vector3r_t &arg0)
+{
+    return sqrt(dot(arg0, arg0));
+}
+
+inline vector3r_t operator*(real_t arg0, const vector3r_t arg1)
+{
+    vector3r_t result = {{arg0 * arg1[0], arg0 * arg1[1], arg0 * arg1[2]}};
+    return result;
+}
+
+inline vector3r_t normalize(const vector3r_t &arg0)
+{
+    return 1.0 / norm(arg0) * arg0;
+}
+
+inline vector2r_t cart2thetaphi(const vector3r_t &cart)
+{
+    real_t r = sqrt(cart[0] * cart[0] + cart[1] * cart[1]);
+    vector2r_t thetaphi = {{Constants::pi_2 - atan2(cart[2], r), atan2(cart[1],
+        cart[0])}};
+    return thetaphi;
+}
+
+inline vector3r_t thetaphi2cart(const vector2r_t &thetaphi)
+{
+    real_t r = sin(thetaphi[0]);
+    vector3r_t cart = {{r * cos(thetaphi[1]), r * sin(thetaphi[1]),
+        cos(thetaphi[0])}};
+    return cart;
+}
+
+// returns az, el, r.
+inline vector3r_t cart2sph(const vector3r_t &cart)
+{
+    real_t r = sqrt(cart[0] * cart[0] + cart[1] * cart[1]);
+
+    vector3r_t sph;
+    sph[0] = atan2(cart[1], cart[0]);
+    sph[1] = atan2(cart[2], r);
+    sph[2] = norm(cart);
+    return sph;
+}
+
+// expects az, el, r.
+inline vector3r_t sph2cart(const vector3r_t &sph)
+{
+    vector3r_t cart = {{sph[2] * cos(sph[1]) * cos(sph[0]), sph[2] * cos(sph[1])
+        * sin(sph[0]), sph[2] * sin(sph[1])}};
+    return cart;
+}
+
+inline matrix22c_t operator*(const matrix22c_t &arg0, const matrix22r_t &arg1)
+{
+    matrix22c_t result;
+    result[0][0] = arg0[0][0] * arg1[0][0] + arg0[0][1] * arg1[1][0];
+    result[0][1] = arg0[0][0] * arg1[0][1] + arg0[0][1] * arg1[1][1];
+    result[1][0] = arg0[1][0] * arg1[0][0] + arg0[1][1] * arg1[1][0];
+    result[1][1] = arg0[1][0] * arg1[0][1] + arg0[1][1] * arg1[1][1];
+    return result;
+}
+
+inline vector3r_t cross(const vector3r_t &arg0, const vector3r_t &arg1)
+{
+    vector3r_t result;
+    result[0] = arg0[1] * arg1[2] - arg0[2] * arg1[1];
+    result[1] = arg0[2] * arg1[0] - arg0[0] * arg1[2];
+    result[2] = arg0[0] * arg1[1] - arg0[1] * arg1[0];
+    return result;
+}
+
+inline vector3r_t operator+(const vector3r_t &arg0, const vector3r_t &arg1)
+{
+    vector3r_t result = {{arg0[0] + arg1[0], arg0[1] + arg1[1],
+        arg0[2] + arg1[2]}};
+    return result;
+}
+
+inline vector3r_t operator-(const vector3r_t &arg0, const vector3r_t &arg1)
+{
+    vector3r_t result = {{arg0[0] - arg1[0], arg0[1] - arg1[1],
+        arg0[2] - arg1[2]}};
+    return result;
+}
+
+inline matrix22c_t normalize(const raw_response_t &raw)
+{
+    matrix22c_t response = {{ {{}}, {{}} }};
+
+    if(raw.weight[0] != 0.0)
+    {
+        response[0][0] = raw.response[0][0] / raw.weight[0];
+        response[0][1] = raw.response[0][1] / raw.weight[0];
+    }
+
+    if(raw.weight[1] != 0.0)
+    {
+        response[1][0] = raw.response[1][0] / raw.weight[1];
+        response[1][1] = raw.response[1][1] / raw.weight[1];
+    }
+
+    return response;
+}
+
+inline diag22c_t normalize(const raw_array_factor_t &raw)
+{
+    diag22c_t af = {{}};
+
+    if(raw.weight[0] != 0.0)
+    {
+        af[0] = raw.factor[0] / raw.weight[0];
+    }
+
+    if(raw.weight[1] != 0.0)
+    {
+        af[1] = raw.factor[1] / raw.weight[1];
+    }
+
+    return af;
+}
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/Station.h b/CEP/Calibration/StationResponse/include/StationResponse/Station.h
new file mode 100644
index 0000000000000000000000000000000000000000..0019c33dcdf0c9577e4b0e9ec9184096812ab710
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/Station.h
@@ -0,0 +1,361 @@
+//# Station.h: Representation of the station beam former.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_STATION_H
+#define LOFAR_STATIONRESPONSE_STATION_H
+
+// \file
+// Representation of the station beam former.
+
+#include <Common/lofar_smartptr.h>
+#include <Common/lofar_string.h>
+#include <Common/lofar_vector.h>
+#include <StationResponse/AntennaField.h>
+#include <StationResponse/Types.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class Station
+{
+public:
+    typedef shared_ptr<Station>             Ptr;
+    typedef shared_ptr<const Station>       ConstPtr;
+    typedef vector<AntennaField::ConstPtr>  FieldList;
+
+    /*!
+     *  \brief Construct a new Station instance.
+     *
+     *  \param name Name of the station.
+     *  \param position Position of the station (ITRF, m).
+     */
+    Station(const string &name, const vector3r_t &position);
+
+    /*!
+     *  \brief Return the name of the station.
+     */
+    const string &name() const;
+
+    /*!
+     *  \brief Return the position of the station (ITRF, m).
+     */
+    const vector3r_t &position() const;
+
+    /*!
+     *  \brief Set the phase reference position. This is the position where the
+     *  delay of the incoming plane wave is assumed to be zero.
+     *
+     *  \param reference Phase reference position (ITRF, m).
+     *
+     *  By default, it is assumed the position of the station is also the phase
+     *  reference position. Use this method to set the phase reference position
+     *  explicitly when this assumption is false.
+     */
+    void setPhaseReference(const vector3r_t &reference);
+
+    /*!
+     *  \brief Return the phase reference position (ITRF, m).
+     *
+     *  \see Station::setPhaseReference()
+     */
+    const vector3r_t &phaseReference() const;
+
+    /*!
+     *  \brief Add an antenna field to the station.
+     *
+     *  Physical %LOFAR stations consist of an LBA field, and either one (remote
+     *  and international stations) or two (core stations) HBA fields. Virtual
+     *  %LOFAR stations can consist of a combination of the antenna fields of
+     *  several physical stations.
+     *
+     *  Use this method to add the appropriate antenna fields to the station.
+     */
+    void addField(const AntennaField::ConstPtr &field);
+
+
+    /*!
+     *  \brief Return the number of available antenna fields.
+     */
+    size_t nFields() const;
+
+    /*!
+     *  \brief Return the requested antenna field.
+     *
+     *  \param i Antenna field number (0-based).
+     *  \return An AntennaField::ConstPtr to the requested AntennaField
+     *  instance, or an empty AntennaField::ConstPtr if \p i is out of bounds.
+     */
+    AntennaField::ConstPtr field(size_t i) const;
+
+    /*!
+     *  \brief Return an iterator that points to the beginning of the list of
+     *  antenna fields.
+     */
+    FieldList::const_iterator beginFields() const;
+
+    /*!
+     *  \brief Return an iterator that points to the end of the list of antenna
+     *  fields.
+     */
+    FieldList::const_iterator endFields() const;
+
+    /*!
+     *  \brief Compute the response of the station for a plane wave of frequency
+     *  \p freq, arriving from direction \p direction, with the %station beam
+     *  former steered towards \p station0, and, for HBA stations, the analog
+     *  %tile beam former steered towards \p tile0. For LBA stations, \p tile0
+     *  has no effect.
+     *
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Frequency of the plane wave (Hz).
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param freq0 %Station beam former reference frequency (Hz).
+     *  \param station0 %Station beam former reference direction (ITRF, m).
+     *  \param tile0 Tile beam former reference direction (ITRF, m).
+     *  \return Jones matrix that represents the %station response.
+     *
+     *  For any given sub-band, the %LOFAR station beam former computes weights
+     *  for a single reference frequency. Usually, this reference frequency is
+     *  the center frequency of the sub-band. For any frequency except the
+     *  reference frequency, these weights are an approximation. This aspect of
+     *  the system is taken into account in the computation of the response.
+     *  Therefore, both the frequency of interest \p freq and the reference
+     *  frequency \p freq0 need to be specified.
+     *
+     *  The directions \p direction, \p station0, and \p tile0 are vectors that
+     *  represent a direction of \e arrival. These vectors have unit length and
+     *  point \e from the ground \e towards the direction from which the plane
+     *  wave arrives.
+     */
+    matrix22c_t response(real_t time, real_t freq, const vector3r_t &direction,
+        real_t freq0, const vector3r_t &station0, const vector3r_t &tile0)
+        const;
+
+    /*!
+     *  \brief Compute the array factor of the station for a plane wave of
+     *  frequency \p freq, arriving from direction \p direction, with the
+     *  %station beam former steered towards \p station0, and, for HBA stations
+     *  the analog %tile beam former steered towards \p tile0. For LBA stations,
+     *  \p tile0 has no effect.
+     *
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Frequency of the plane wave (Hz).
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param freq0 %Station beam former reference frequency (Hz).
+     *  \param station0 %Station beam former reference direction (ITRF, m).
+     *  \param tile0 Tile beam former reference direction (ITRF, m).
+     *  \return A diagonal matrix with the array factor of the X and Y antennae.
+     *
+     *  For any given sub-band, the %LOFAR station beam former computes weights
+     *  for a single reference frequency. Usually, this reference frequency is
+     *  the center frequency of the sub-band. For any frequency except the
+     *  reference frequency, these weights are an approximation. This aspect of
+     *  the system is taken into account in the computation of the response.
+     *  Therefore, both the frequency of interest \p freq and the reference
+     *  frequency \p freq0 need to be specified.
+     *
+     *  The directions \p direction, \p station0, and \p tile0 are vectors that
+     *  represent a direction of \e arrival. These vectors have unit length and
+     *  point \e from the ground \e towards the direction from which the plane
+     *  wave arrives.
+     */
+    diag22c_t arrayFactor(real_t time, real_t freq, const vector3r_t &direction,
+        real_t freq0, const vector3r_t &station0, const vector3r_t &tile0)
+        const;
+
+    /*!
+     *  \name Convenience member functions
+     *  These member functions perform the same function as the corresponding
+     *  non-template member functions, for a list of frequencies or (frequency,
+     *  reference frequency) pairs.
+     */
+    // @{
+
+    /*!
+     *  \brief Convenience method to compute the response of the station for a
+     *  list of frequencies, and a fixed reference frequency.
+     *
+     *  \param count Number of frequencies.
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Input iterator for a list of frequencies (Hz) of length
+     *  \p count.
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param freq0 %Station beam former reference frequency (Hz).
+     *  \param station0 %Station beam former reference direction (ITRF, m).
+     *  \param tile0 Tile beam former reference direction (ITRF, m).
+     *  \param buffer Output iterator with room for \p count instances of type
+     *  ::matrix22c_t.
+     *
+     *  \see response(real_t time, real_t freq, const vector3r_t &direction,
+     *  real_t freq0, const vector3r_t &station0, const vector3r_t &tile0) const
+     */
+    template <typename T, typename U>
+    void response(unsigned int count, real_t time, T freq,
+        const vector3r_t &direction, real_t freq0, const vector3r_t &station0,
+        const vector3r_t &tile0, U buffer) const;
+
+    /*!
+     *  \brief Convenience method to compute the array factor of the station for
+     *  a list of frequencies, and a fixed reference frequency.
+     *
+     *  \param count Number of frequencies.
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Input iterator for a list of frequencies (Hz) of length
+     *  \p count.
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param freq0 %Station beam former reference frequency (Hz).
+     *  \param station0 %Station beam former reference direction (ITRF, m).
+     *  \param tile0 Tile beam former reference direction (ITRF, m).
+     *  \param buffer Output iterator with room for \p count instances of type
+     *  ::diag22c_t.
+     *
+     *  \see arrayFactor(real_t time, real_t freq, const vector3r_t &direction,
+     *  real_t freq0, const vector3r_t &station0, const vector3r_t &tile0) const
+     */
+    template <typename T, typename U>
+    void arrayFactor(unsigned int count, real_t time, T freq,
+        const vector3r_t &direction, real_t freq0, const vector3r_t &station0,
+        const vector3r_t &tile0, U buffer) const;
+
+    /*!
+     *  \brief Convenience method to compute the response of the station for a
+     *  list of (frequency, reference frequency) pairs.
+     *
+     *  \param count Number of frequencies.
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Input iterator for a list of frequencies (Hz) of length
+     *  \p count.
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param freq0 Input iterator for a list of %Station beam former reference
+     *  frequencies (Hz) of length \p count.
+     *  \param station0 %Station beam former reference direction (ITRF, m).
+     *  \param tile0 Tile beam former reference direction (ITRF, m).
+     *  \param buffer Output iterator with room for \p count instances of type
+     *  ::matrix22c_t.
+     *
+     *  \see response(real_t time, real_t freq, const vector3r_t &direction,
+     *  real_t freq0, const vector3r_t &station0, const vector3r_t &tile0) const
+     */
+    template <typename T, typename U>
+    void response(unsigned int count, real_t time, T freq,
+        const vector3r_t &direction, T freq0, const vector3r_t &station0,
+        const vector3r_t &tile0, U buffer) const;
+
+    /*!
+     *  \brief Convenience method to compute the array factor of the station for
+     *  list of (frequency, reference frequency) pairs.
+     *
+     *  \param count Number of frequencies.
+     *  \param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s).
+     *  \param freq Input iterator for a list of frequencies (Hz) of length
+     *  \p count.
+     *  \param direction Direction of arrival (ITRF, m).
+     *  \param freq0 %Station beam former reference frequency (Hz).
+     *  \param station0 %Station beam former reference direction (ITRF, m).
+     *  \param tile0 Tile beam former reference direction (ITRF, m).
+     *  \param buffer Output iterator with room for \p count instances of type
+     *  ::diag22c_t.
+     *
+     *  \see arrayFactor(real_t time, real_t freq, const vector3r_t &direction,
+     *  real_t freq0, const vector3r_t &station0, const vector3r_t &tile0) const
+     */
+    template <typename T, typename U>
+    void arrayFactor(unsigned int count, real_t time, T freq,
+        const vector3r_t &direction, T freq0, const vector3r_t &station0,
+        const vector3r_t &tile0, U buffer) const;
+
+    // @}
+
+private:
+    raw_array_factor_t fieldArrayFactor(const AntennaField::ConstPtr &field,
+        real_t time, real_t freq, const vector3r_t &direction, real_t freq0,
+        const vector3r_t &position0, const vector3r_t &direction0) const;
+
+private:
+    string      itsName;
+    vector3r_t  itsPosition;
+    vector3r_t  itsPhaseReference;
+    FieldList   itsFields;
+};
+
+// @}
+
+//# ------------------------------------------------------------------------- //
+//# - Implementation: Station                                               - //
+//# ------------------------------------------------------------------------- //
+
+template <typename T, typename U>
+void Station::response(unsigned int count, real_t time, T freq,
+    const vector3r_t &direction, real_t freq0, const vector3r_t &station0,
+    const vector3r_t &tile0, U buffer) const
+{
+    for(unsigned int i = 0; i < count; ++i)
+    {
+        *buffer++ = response(time, *freq++, direction, freq0, station0, tile0);
+    }
+}
+
+template <typename T, typename U>
+void Station::arrayFactor(unsigned int count, real_t time, T freq,
+    const vector3r_t &direction, real_t freq0, const vector3r_t &station0,
+    const vector3r_t &tile0, U buffer) const
+{
+    for(unsigned int i = 0; i < count; ++i)
+    {
+        *buffer++ = arrayFactor(time, *freq++, direction, freq0, station0,
+            tile0);
+    }
+}
+
+template <typename T, typename U>
+void Station::response(unsigned int count, real_t time, T freq,
+    const vector3r_t &direction, T freq0, const vector3r_t &station0,
+    const vector3r_t &tile0, U buffer) const
+{
+    for(unsigned int i = 0; i < count; ++i)
+    {
+        *buffer++ = response(time, *freq++, direction, *freq0++, station0,
+            tile0);
+    }
+}
+
+template <typename T, typename U>
+void Station::arrayFactor(unsigned int count, real_t time, T freq,
+    const vector3r_t &direction, T freq0, const vector3r_t &station0,
+    const vector3r_t &tile0, U buffer) const
+{
+    for(unsigned int i = 0; i < count; ++i)
+    {
+        *buffer++ = arrayFactor(time, *freq++, direction, *freq0++, station0,
+            tile0);
+    }
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/TileAntenna.h b/CEP/Calibration/StationResponse/include/StationResponse/TileAntenna.h
new file mode 100644
index 0000000000000000000000000000000000000000..3695aa3e81657a3bcdbe5bfb91726287d5cf9040
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/TileAntenna.h
@@ -0,0 +1,68 @@
+//# TileAntenna.h: Semi-analytical model of a LOFAR HBA tile.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_TILEANTENNA_H
+#define LOFAR_STATIONRESPONSE_TILEANTENNA_H
+
+// \file
+// Semi-analytical model of a LOFAR HBA tile.
+
+#include <StationResponse/AntennaModelHBA.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+class TileAntenna: public AntennaModelHBA
+{
+public:
+    typedef shared_ptr<TileAntenna>         Ptr;
+    typedef shared_ptr<const TileAntenna>   ConstPtr;
+
+    typedef static_array<vector3r_t, 16>    TileConfig;
+
+    explicit TileAntenna(const TileConfig &config);
+
+    void setConfig(const TileConfig &config);
+
+    const TileConfig &config() const;
+
+    virtual raw_array_factor_t rawArrayFactor(real_t freq,
+        const vector3r_t &direction, const vector3r_t &direction0) const;
+
+    virtual matrix22c_t elementResponse(real_t freq,
+        const vector3r_t &direction) const;
+
+private:
+    TileConfig  itsConfig;
+};
+
+// @}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/include/StationResponse/Types.h b/CEP/Calibration/StationResponse/include/StationResponse/Types.h
new file mode 100644
index 0000000000000000000000000000000000000000..8565e127528dc692ae7750e4b9c4ee09c7cad037
--- /dev/null
+++ b/CEP/Calibration/StationResponse/include/StationResponse/Types.h
@@ -0,0 +1,234 @@
+//# Types.h: Types used in this library.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#ifndef LOFAR_STATIONRESPONSE_TYPES_H
+#define LOFAR_STATIONRESPONSE_TYPES_H
+
+// \file
+// Types used in this library.
+
+#include <Common/lofar_complex.h>
+#include <Common/StreamUtil.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+// \addtogroup StationResponse
+// @{
+
+/**
+ *  \brief Array of static size.
+ *
+ *  This struct wraps a (unidimensional) C array. Like C arrays, it is possible
+ *  to use initializers, for example:
+ *  \code
+ *  int foo[3] = {1, 2, 3};
+ *  static_array<int, 3> bar = {{1, 2, 3}};
+ *  \endcode
+ *  Note the \e double curly braces.
+ *
+ *  Unlike C arrays, it is possible to return instances of this struct.
+ *  \code
+ *  static_array<int, 3> foo()
+ *  {
+ *      static_array<int, 3> bar = {{1, 2, 3}};
+ *      return bar;
+ *  }
+ *  \endcode
+ *  \see boost::array
+ */
+template <typename T, size_t N>
+struct static_array
+{
+    typedef T       *iterator;
+    typedef const T *const_iterator;
+
+    T  __data[N];
+
+    /** Returns the size of the array. */
+    static size_t size();
+
+    /**
+     *  \brief Read-only access to a specific array element.
+     *  \param n Index (0-based) of the element to access.
+     *  \return A constant reference to the element at index \p n.
+     *
+     *  This operator provides array-style element access. No range check is
+     *  performed on the index \p n. The result of this operator is undefined
+     *  for out of range indices.
+     */
+    const T &operator[](size_t n) const;
+
+    /**
+     *  \brief Access to a specific array element.
+     *  \param n Index (0-based) of the element to access.
+     *  \return A non-constant reference to the element at index \p n.
+     *
+     *  This operator provides array-style element access. No range check is
+     *  performed on the index \p n. The result of this operator is undefined
+     *  for out of range indices.
+     */
+    T &operator[](size_t n);
+
+    /** Returns an iterator that points to the first element in the array. */
+    iterator begin();
+
+    /** Returns an iterator that points one past the last  element in the
+     *  array.
+     */
+    iterator end();
+
+    /** Returns a read-only iterator that points to the first element in the
+     *  array.
+     */
+    const_iterator begin() const;
+
+    /** Returns a read-only iterator that points one past the last element in
+     *  the array.
+     */
+    const_iterator end() const;
+};
+
+/** Print the contents of a static array. */
+template <typename T, size_t N>
+ostream &operator<<(ostream &out, const static_array<T,N> &obj);
+
+/** Type used for real scalars. */
+typedef double                                      real_t;
+
+/** Type used for complex scalars. */
+typedef dcomplex                                    complex_t;
+
+/** Type used for 2-dimensional real vectors. */
+typedef static_array<real_t, 2>                     vector2r_t;
+
+/** Type used for 3-dimensional real vectors. */
+typedef static_array<real_t, 3>                     vector3r_t;
+
+/** Type used for 2x2 real diagonal matrices. */
+typedef static_array<real_t, 2>                     diag22r_t;
+
+/** Type used for 2x2 complex diagonal matrices. */
+typedef static_array<complex_t, 2>                  diag22c_t;
+
+/** Type used for 2x2 real matrices. */
+typedef static_array<static_array<real_t, 2>, 2>    matrix22r_t;
+
+/** Type used for 2x2 complex matrices. */
+typedef static_array<static_array<complex_t, 2>, 2> matrix22c_t;
+
+/** Response of an array of antenna elements. */
+struct raw_response_t
+{
+    /** Combined response of all (enabled) antenna elements in the array. */
+    matrix22c_t response;
+
+    /** Number of antenna elements contributing to the combined response, per
+     *  polarization.
+     */
+    diag22r_t   weight;
+};
+
+/** Array factor of an array of antenna elements. A wave front of an incident
+ *  plane wave will arrive at each antenna element at a potentially different
+ *  time. The time of arrival depends on the location of the antenna element and
+ *  the direction of arrival of the plane wave. With respect to a pre-defined
+ *  phase reference location, there is a (possibly negative) delay between the
+ *  arrival of a wave front at a given antenna element and the arrival of the
+ *  same wave front at the phase reference location. The array factor is the sum
+ *  of the phase shifts due to these delays. It describes the "sensitivity" of
+ *  the array as a function of direction.
+ */
+struct raw_array_factor_t
+{
+    /** Array factor due to all (enabled) antenna elements in the array. */
+    diag22c_t   factor;
+
+    /** Number of antenna elements contributing to the array factor, per
+     *  polarization.
+     */
+    diag22r_t   weight;
+};
+
+// @}
+
+//# ------------------------------------------------------------------------- //
+//# - Implementation: static_array                                          - //
+//# ------------------------------------------------------------------------- //
+
+template <typename T, size_t N>
+inline const T &static_array<T, N>::operator[](size_t n) const
+{
+    return __data[n];
+}
+
+template <typename T, size_t N>
+inline T &static_array<T, N>::operator[](size_t n)
+{
+    return __data[n];
+}
+
+template <typename T, size_t N>
+inline typename static_array<T, N>::iterator static_array<T, N>::begin()
+{
+    return __data;
+}
+
+template <typename T, size_t N>
+inline typename static_array<T, N>::iterator static_array<T, N>::end()
+{
+    return __data + N;
+}
+
+template <typename T, size_t N>
+inline typename static_array<T, N>::const_iterator static_array<T, N>::begin()
+    const
+{
+    return __data;
+}
+
+template <typename T, size_t N>
+inline typename static_array<T, N>::const_iterator static_array<T, N>::end()
+    const
+{
+    return __data + N;
+}
+
+template <typename T, size_t N>
+inline size_t static_array<T, N>::size()
+{
+    return N;
+}
+
+template <typename T, size_t N>
+ostream &operator<<(ostream &out, const static_array<T,N> &obj)
+{
+  print(out, obj.begin(), obj.end());
+  return out;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
+
+#endif
diff --git a/CEP/Calibration/StationResponse/src/AntennaField.cc b/CEP/Calibration/StationResponse/src/AntennaField.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d3ca5879353d523a70109c631d1046055f08b713
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/AntennaField.cc
@@ -0,0 +1,233 @@
+//# AntennaField.cc: Representation of a LOFAR antenna field, with methods to
+//# compute its response to incoming radiation.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/AntennaField.h>
+#include <StationResponse/Constants.h>
+#include <StationResponse/MathUtil.h>
+#include <ElementResponse/ElementResponse.h>
+#include <casacore/measures/Measures/MeasFrame.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+AntennaField::AntennaField(const string &name,
+    const CoordinateSystem &coordinates)
+    :   itsName(name),
+        itsCoordinateSystem(coordinates),
+        itsNCPCacheTime(-1)
+{
+    vector3r_t ncp = {{0.0, 0.0, 1.0}};
+    itsNCP.reset(new ITRFDirection(position(), ncp));
+}
+
+AntennaField::~AntennaField()
+{
+}
+
+const string &AntennaField::name() const
+{
+    return itsName;
+}
+
+const vector3r_t &AntennaField::position() const
+{
+    return itsCoordinateSystem.origin;
+}
+
+const AntennaField::CoordinateSystem &AntennaField::coordinates() const
+{
+    return itsCoordinateSystem;
+}
+
+void AntennaField::addAntenna(const Antenna &antenna)
+{
+    itsAntennae.push_back(antenna);
+}
+
+size_t AntennaField::nAntennae() const
+{
+    return itsAntennae.size();
+}
+
+const AntennaField::Antenna &AntennaField::antenna(size_t n) const
+{
+    return itsAntennae[n];
+}
+
+AntennaField::Antenna &AntennaField::antenna(size_t n)
+{
+    return itsAntennae[n];
+}
+
+AntennaField::AntennaList::const_iterator AntennaField::beginAntennae() const
+{
+    return itsAntennae.begin();
+}
+
+AntennaField::AntennaList::const_iterator AntennaField::endAntennae() const
+{
+    return itsAntennae.end();
+}
+
+vector3r_t AntennaField::ncp(real_t time) const
+{
+    if(time != itsNCPCacheTime)
+    {
+        itsNCPCacheDirection = itsNCP->at(time);
+        itsNCPCacheTime = time;
+    }
+
+    return itsNCPCacheDirection;
+}
+
+vector3r_t AntennaField::itrf2field(const vector3r_t &itrf) const
+{
+    const CoordinateSystem::Axes &axes = itsCoordinateSystem.axes;
+    vector3r_t station = {{dot(axes.p, itrf), dot(axes.q, itrf),
+        dot(axes.r, itrf)}};
+    return station;
+}
+
+matrix22r_t AntennaField::rotation(real_t time, const vector3r_t &direction)
+    const
+{
+    // Compute the cross product of the NCP and the target direction. This
+    // yields a vector tangent to the celestial sphere at the target
+    // direction, pointing towards the East (the direction of +Y in the IAU
+    // definition, or positive right ascension).
+    // Test if the direction is equal to the NCP. If it is, take a random
+    // vector orthogonal to v1 (the east is not defined here).
+    vector3r_t v1;
+    if (abs(ncp(time)[0]-direction[0])<1e-9 &&
+        abs(ncp(time)[1]-direction[1])<1e-9 &&
+        abs(ncp(time)[2]-direction[2])<1e-9) {
+        // Make sure v1 is orthogonal to ncp(time). The first two components
+        // of v1 are arbitrary.
+        v1[0]=1.;
+        v1[1]=0.;
+        v1[2]=-(ncp(time)[0]*v1[0]+ncp(time)[1]*v1[1])/ncp(time)[2];
+        v1=normalize(v1);
+    } else {
+        v1 = normalize(cross(ncp(time), direction));
+    }
+
+    // Compute the cross product of the antenna field normal (R) and the
+    // target direction. This yields a vector tangent to the topocentric
+    // spherical coordinate system at the target direction, pointing towards
+    // the direction of positive phi (which runs East over North around the
+    // pseudo zenith).
+    // Test if the normal is equal to the target direction. If it is, take
+    // a random vector orthogonal to the normal.
+    vector3r_t v2;
+    if (abs(itsCoordinateSystem.axes.r[0]-direction[0])<1e-9 &&
+        abs(itsCoordinateSystem.axes.r[1]-direction[1])<1e-9 &&
+        abs(itsCoordinateSystem.axes.r[2]-direction[2])<1e-9) {
+        // Make sure v2 is orthogonal to r. The first two components
+        // of v2 are arbitrary.
+        v2[0]=1.;
+        v2[1]=0.;
+        v2[2]=-(itsCoordinateSystem.axes.r[0]*v2[0]+
+                itsCoordinateSystem.axes.r[1]*v2[1])/
+                itsCoordinateSystem.axes.r[2];
+        v2=normalize(v2);
+    } else {
+        v2 = normalize(cross(itsCoordinateSystem.axes.r, direction));
+    }
+
+    // Compute the cosine and sine of the parallactic angle, i.e. the angle
+    // between v1 and v2, both tangent to a latitude circle of their
+    // respective spherical coordinate systems.
+    real_t coschi = dot(v1, v2);
+    real_t sinchi = dot(cross(v1, v2), direction);
+
+    // The input coordinate system is a right handed system with its third
+    // axis along the direction of propagation (IAU +Z). The output
+    // coordinate system is right handed as well, but its third axis points
+    // in the direction of arrival (i.e. exactly opposite).
+    //
+    // Because the electromagnetic field is always perpendicular to the
+    // direction of propagation, we only need to relate the (X, Y) axes of
+    // the input system to the corresponding (theta, phi) axes of the output
+    // system.
+    //
+    // To this end, we first rotate the input system around its third axis
+    // to align the Y axis with the phi axis. The X and theta axis are
+    // parallel after this rotation, but point in opposite directions. To
+    // align the X axis with the theta axis, we flip it.
+    //
+    // The Jones matrix to align the Y axis with the phi axis when these are
+    // separated by an angle phi (measured counter-clockwise around the
+    // direction of propagation, looking towards the origin), is given by:
+    //
+    // [ cos(phi)  sin(phi)]
+    // [-sin(phi)  cos(phi)]
+    //
+    // Here, cos(phi) and sin(phi) can be computed directly, without having
+    // to compute phi first (see the computation of coschi and sinchi
+    // above).
+    //
+    // Now, sinchi as computed above is opposite to sin(phi), because the
+    // direction used in the computation is the direction of arrival instead
+    // of the direction of propagation. Therefore, the sign of sinchi needs
+    // to be reversed. Furthermore, as explained above, the X axis has to be
+    // flipped to align with the theta axis. The Jones matrix returned from
+    // this function is therefore given by:
+    //
+    // [-coschi  sinchi]
+    // [ sinchi  coschi]
+    matrix22r_t rotation = {{{{-coschi, sinchi}}, {{sinchi, coschi}}}};
+    return rotation;
+}
+
+matrix22c_t AntennaField::response(real_t time, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    return normalize(rawResponse(time, freq, direction, direction0));
+}
+
+diag22c_t AntennaField::arrayFactor(real_t time, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    return normalize(rawArrayFactor(time, freq, direction, direction0));
+}
+
+raw_response_t AntennaField::rawResponse(real_t time, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    raw_array_factor_t af = rawArrayFactor(time, freq, direction, direction0);
+
+    raw_response_t result;
+    result.response = elementResponse(time, freq, direction);
+    result.response[0][0] *= af.factor[0];
+    result.response[0][1] *= af.factor[0];
+    result.response[1][0] *= af.factor[1];
+    result.response[1][1] *= af.factor[1];
+    result.weight = af.weight;
+    return result;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/AntennaFieldHBA.cc b/CEP/Calibration/StationResponse/src/AntennaFieldHBA.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ff099fb04a22ea2987f0f8c397f353bb69b66e6a
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/AntennaFieldHBA.cc
@@ -0,0 +1,77 @@
+//# AntennaFieldHBA.cc: Representation of an HBA antenna field.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/AntennaFieldHBA.h>
+#include <StationResponse/MathUtil.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+AntennaFieldHBA::AntennaFieldHBA(const string &name,
+    const CoordinateSystem &coordinates, const AntennaModelHBA::ConstPtr &model)
+    :   AntennaField(name, coordinates),
+        itsAntennaModel(model)
+{
+}
+
+matrix22c_t AntennaFieldHBA::response(real_t time, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    return itsAntennaModel->response(freq, itrf2field(direction),
+        itrf2field(direction0)) * rotation(time, direction);
+}
+
+diag22c_t AntennaFieldHBA::arrayFactor(real_t, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    return itsAntennaModel->arrayFactor(freq, itrf2field(direction),
+        itrf2field(direction0));
+}
+
+raw_response_t AntennaFieldHBA::rawResponse(real_t time, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    raw_response_t result = itsAntennaModel->rawResponse(freq,
+        itrf2field(direction), itrf2field(direction0));
+    result.response = result.response * rotation(time, direction);
+    return result;
+}
+
+raw_array_factor_t AntennaFieldHBA::rawArrayFactor(real_t, real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    return itsAntennaModel->rawArrayFactor(freq, itrf2field(direction),
+        itrf2field(direction0));
+}
+
+matrix22c_t AntennaFieldHBA::elementResponse(real_t time, real_t freq,
+    const vector3r_t &direction) const
+{
+    return itsAntennaModel->elementResponse(freq, itrf2field(direction))
+        * rotation(time, direction);
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/AntennaFieldLBA.cc b/CEP/Calibration/StationResponse/src/AntennaFieldLBA.cc
new file mode 100644
index 0000000000000000000000000000000000000000..90838c135e6790e2a9d757bf6a108a24d3933059
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/AntennaFieldLBA.cc
@@ -0,0 +1,54 @@
+//# AntennaFieldLBA.cc: Representation of an LBA antenna field.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/AntennaFieldLBA.h>
+#include <StationResponse/MathUtil.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+AntennaFieldLBA::AntennaFieldLBA(const string &name,
+    const CoordinateSystem &coordinates, const AntennaModelLBA::ConstPtr &model)
+    :   AntennaField(name, coordinates),
+        itsAntennaModel(model)
+{
+}
+
+raw_array_factor_t AntennaFieldLBA::rawArrayFactor(real_t, real_t,
+    const vector3r_t&, const vector3r_t&) const
+{
+    raw_array_factor_t af = {{{1.0, 1.0}}, {{1.0, 1.0}}};
+    return af;
+}
+
+matrix22c_t AntennaFieldLBA::elementResponse(real_t time, real_t freq,
+    const vector3r_t &direction) const
+{
+    return itsAntennaModel->response(freq, itrf2field(direction))
+        * rotation(time, direction);
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/AntennaModelHBA.cc b/CEP/Calibration/StationResponse/src/AntennaModelHBA.cc
new file mode 100644
index 0000000000000000000000000000000000000000..52ca0accc5428c38aa73964bd74046d36dcda223
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/AntennaModelHBA.cc
@@ -0,0 +1,64 @@
+//# AntennaModelHBA.cc: HBA antenna model interface definitions.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/AntennaModelHBA.h>
+#include <StationResponse/MathUtil.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+AntennaModelHBA::~AntennaModelHBA()
+{
+}
+
+matrix22c_t AntennaModelHBA::response(real_t freq, const vector3r_t &direction,
+    const vector3r_t &direction0) const
+{
+    return normalize(rawResponse(freq, direction, direction0));
+}
+
+diag22c_t AntennaModelHBA::arrayFactor(real_t freq, const vector3r_t &direction,
+    const vector3r_t &direction0) const
+{
+    return normalize(rawArrayFactor(freq, direction, direction0));
+}
+
+raw_response_t AntennaModelHBA::rawResponse(real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    raw_array_factor_t af = rawArrayFactor(freq, direction, direction0);
+
+    raw_response_t result;
+    result.response = elementResponse(freq, direction);
+    result.response[0][0] *= af.factor[0];
+    result.response[0][1] *= af.factor[0];
+    result.response[1][0] *= af.factor[1];
+    result.response[1][1] *= af.factor[1];
+    result.weight = af.weight;
+    return result;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/AntennaModelLBA.cc b/CEP/Calibration/StationResponse/src/AntennaModelLBA.cc
new file mode 100644
index 0000000000000000000000000000000000000000..87d02b945013406efe05d18c4481dc98a2651d42
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/AntennaModelLBA.cc
@@ -0,0 +1,36 @@
+//# AntennaModelLBA.cc: LBA antenna model interface definitions.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/AntennaModelLBA.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+AntennaModelLBA::~AntennaModelLBA()
+{
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/CMakeLists.txt b/CEP/Calibration/StationResponse/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e31a7a54574b0e908c6ba2760cf49626696f1850
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/CMakeLists.txt
@@ -0,0 +1,20 @@
+# $Id$
+
+include(LofarPackageVersion)
+
+lofar_add_library(stationresponse
+  Package__Version.cc
+  AntennaField.cc
+  AntennaFieldHBA.cc
+  AntennaFieldLBA.cc
+  AntennaModelHBA.cc
+  AntennaModelLBA.cc
+  DualDipoleAntenna.cc
+  ITRFDirection.cc
+  LofarMetaDataUtil.cc
+  MathUtil.cc
+  Station.cc
+  TileAntenna.cc
+  Types.cc)
+
+lofar_add_bin_program(makeresponseimage makeresponseimage.cc)
diff --git a/CEP/Calibration/StationResponse/src/DualDipoleAntenna.cc b/CEP/Calibration/StationResponse/src/DualDipoleAntenna.cc
new file mode 100644
index 0000000000000000000000000000000000000000..659d9f45ddb16ad04a66678d36fad174439fa152
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/DualDipoleAntenna.cc
@@ -0,0 +1,51 @@
+//# DualDipoleAntenna.cc: Semi-analytical model of a LOFAR LBA dual dipole
+//# antenna.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/DualDipoleAntenna.h>
+#include <StationResponse/Constants.h>
+#include <StationResponse/MathUtil.h>
+#include <ElementResponse/ElementResponse.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+matrix22c_t DualDipoleAntenna::response(real_t freq,
+    const vector3r_t &direction) const
+{
+    // The positive X dipole direction is SW of the reference orientation,
+    // which translates to a phi coordinate of 5/4*pi in the topocentric
+    // spherical coordinate system. The phi coordinate is corrected for this
+    // offset before evaluating the antenna model.
+    vector2r_t thetaphi = cart2thetaphi(direction);
+    thetaphi[1] -= 5.0 * Constants::pi_4;
+    matrix22c_t response;
+    element_response_lba(freq, thetaphi[0], thetaphi[1],
+        reinterpret_cast<std::complex<double> (&)[2][2]>(response));
+    return response;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/ITRFDirection.cc b/CEP/Calibration/StationResponse/src/ITRFDirection.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f7f9c49e02551bfac1b6afbdeb0c14acbeaa449d
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/ITRFDirection.cc
@@ -0,0 +1,77 @@
+//# ITRFDirection.cc: Functor that maps time to an ITRF direction.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/ITRFDirection.h>
+
+#include <casacore/measures/Measures/MPosition.h>
+#include <casacore/measures/Measures/MDirection.h>
+#include <casacore/measures/Measures/MEpoch.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+ITRFDirection::ITRFDirection(const vector3r_t &position,
+    const vector2r_t &direction)
+{
+    casacore::MVPosition mvPosition(position[0], position[1], position[2]);
+    casacore::MPosition mPosition(mvPosition, casacore::MPosition::ITRF);
+    itsFrame = casacore::MeasFrame(casacore::MEpoch(), mPosition);
+
+    // Order of angles seems to be longitude (along the equator), lattitude
+    // (towards the pole).
+    casacore::MVDirection mvDirection(direction[0], direction[1]);
+    casacore::MDirection mDirection(mvDirection, casacore::MDirection::J2000);
+    itsConverter = casacore::MDirection::Convert(mDirection,
+        casacore::MDirection::Ref(casacore::MDirection::ITRF, itsFrame));
+}
+
+ITRFDirection::ITRFDirection(const vector3r_t &position,
+    const vector3r_t &direction)
+{
+    casacore::MVPosition mvPosition(position[0], position[1], position[2]);
+    casacore::MPosition mPosition(mvPosition, casacore::MPosition::ITRF);
+    itsFrame = casacore::MeasFrame(casacore::MEpoch(), mPosition);
+
+    casacore::MVDirection mvDirection(direction[0], direction[1], direction[2]);
+    casacore::MDirection mDirection(mvDirection, casacore::MDirection::J2000);
+    itsConverter = casacore::MDirection::Convert(mDirection,
+        casacore::MDirection::Ref(casacore::MDirection::ITRF, itsFrame));
+}
+
+vector3r_t ITRFDirection::at(real_t time) const
+{
+    // Cannot use MeasFrame::resetEpoch(Double), because that assumes the
+    // argument is UTC in (fractional) days (MJD).
+    itsFrame.resetEpoch(casacore::Quantity(time, "s"));
+
+    const casacore::MDirection &mITRF = itsConverter();
+    const casacore::MVDirection &mvITRF = mITRF.getValue();
+
+    vector3r_t itrf = {{mvITRF(0), mvITRF(1), mvITRF(2)}};
+    return itrf;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/LofarMetaDataUtil.cc b/CEP/Calibration/StationResponse/src/LofarMetaDataUtil.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4d96af8dfe4f3f0c1b7b945d625e66bc5d977282
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/LofarMetaDataUtil.cc
@@ -0,0 +1,335 @@
+//# LofarMetaDataUtil.cc: Utility functions to read the meta data relevant for
+//# simulating the beam from LOFAR observations stored in MS format.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/LofarMetaDataUtil.h>
+#include <StationResponse/AntennaFieldLBA.h>
+#include <StationResponse/AntennaFieldHBA.h>
+#include <StationResponse/MathUtil.h>
+#include <StationResponse/TileAntenna.h>
+#include <StationResponse/DualDipoleAntenna.h>
+
+#include <casacore/measures/Measures/MDirection.h>
+#include <casacore/measures/Measures/MPosition.h>
+#include <casacore/measures/Measures/MCDirection.h>
+#include <casacore/measures/Measures/MCPosition.h>
+#include <casacore/measures/Measures/MeasTable.h>
+#include <casacore/measures/Measures/MeasConvert.h>
+#include <casacore/measures/TableMeasures/ScalarMeasColumn.h>
+
+#include <stdexcept>
+
+#include <casacore/ms/MeasurementSets/MSAntenna.h>
+#include <casacore/ms/MSSel/MSSelection.h>
+#include <casacore/ms/MSSel/MSAntennaParse.h>
+#include <casacore/ms/MeasurementSets/MSAntennaColumns.h>
+#include <casacore/ms/MeasurementSets/MSDataDescription.h>
+#include <casacore/ms/MeasurementSets/MSDataDescColumns.h>
+#include <casacore/ms/MeasurementSets/MSField.h>
+#include <casacore/ms/MeasurementSets/MSFieldColumns.h>
+#include <casacore/ms/MeasurementSets/MSObservation.h>
+#include <casacore/ms/MeasurementSets/MSObsColumns.h>
+#include <casacore/ms/MeasurementSets/MSPolarization.h>
+#include <casacore/ms/MeasurementSets/MSPolColumns.h>
+#include <casacore/ms/MeasurementSets/MSSpectralWindow.h>
+#include <casacore/ms/MeasurementSets/MSSpWindowColumns.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+using namespace casacore;
+
+bool hasColumn(const Table &table, const string &column)
+{
+    return table.tableDesc().isColumn(column);
+}
+
+bool hasSubTable(const Table &table, const string &name)
+{
+    return table.keywordSet().isDefined(name);
+}
+
+Table getSubTable(const Table &table, const string &name)
+{
+    return table.keywordSet().asTable(name);
+}
+
+TileAntenna::TileConfig readTileConfig(const Table &table, unsigned int row)
+{
+    ROArrayQuantColumn<Double> c_tile_offset(table, "TILE_ELEMENT_OFFSET", "m");
+
+    // Read tile configuration for HBA antenna fields.
+    Matrix<Quantity> aips_offset = c_tile_offset(row);
+    assert(aips_offset.ncolumn() == TileAntenna::TileConfig::size());
+
+    TileAntenna::TileConfig config;
+    for(unsigned int i = 0; i < config.size(); ++i)
+    {
+        config[i][0] = aips_offset(0, i).getValue();
+        config[i][1] = aips_offset(1, i).getValue();
+        config[i][2] = aips_offset(2, i).getValue();
+    }
+    return config;
+}
+
+void transformToFieldCoordinates(TileAntenna::TileConfig &config,
+    const AntennaField::CoordinateSystem::Axes &axes)
+{
+    for(unsigned int i = 0; i < config.size(); ++i)
+    {
+        const vector3r_t position = config[i];
+        config[i][0] = dot(position, axes.p);
+        config[i][1] = dot(position, axes.q);
+        config[i][2] = dot(position, axes.r);
+    }
+}
+
+AntennaField::CoordinateSystem readCoordinateSystemAartfaac(
+    const Table &table, unsigned int id)
+{
+    ROArrayQuantColumn<Double> c_position(table, "POSITION", "m");
+ 
+    // Read antenna field center (ITRF).
+    Vector<Quantity> aips_position = c_position(id);
+    assert(aips_position.size() == 3);
+
+    vector3r_t position = {{aips_position(0).getValue(),
+        aips_position(1).getValue(), aips_position(2).getValue()}};
+
+    TableRecord keywordset = table.keywordSet();
+    Matrix<double> aips_axes;
+    keywordset.get("AARTFAAC_COORDINATE_AXES", aips_axes);
+    assert(aips_axes.shape().isEqual(IPosition(2, 3, 3)));
+
+    vector3r_t p = {{aips_axes(0, 0), aips_axes(1, 0), aips_axes(2, 0)}};
+    vector3r_t q = {{aips_axes(0, 1), aips_axes(1, 1), aips_axes(2, 1)}};
+    vector3r_t r = {{aips_axes(0, 2), aips_axes(1, 2), aips_axes(2, 2)}};
+
+    AntennaField::CoordinateSystem system = {position, {p, q, r}};
+
+    return system;
+}
+
+AntennaField::CoordinateSystem readCoordinateSystem(const Table &table,
+    unsigned int id)
+{
+    ROArrayQuantColumn<Double> c_position(table, "POSITION", "m");
+    ROArrayQuantColumn<Double> c_axes(table, "COORDINATE_AXES", "m");
+
+    // Read antenna field center (ITRF).
+    Vector<Quantity> aips_position = c_position(id);
+    assert(aips_position.size() == 3);
+
+    vector3r_t position = {{aips_position(0).getValue(),
+        aips_position(1).getValue(), aips_position(2).getValue()}};
+
+    // Read antenna field coordinate axes (ITRF).
+    Matrix<Quantity> aips_axes = c_axes(id);
+    assert(aips_axes.shape().isEqual(IPosition(2, 3, 3)));
+
+    vector3r_t p = {{aips_axes(0, 0).getValue(), aips_axes(1, 0).getValue(),
+        aips_axes(2, 0).getValue()}};
+    vector3r_t q = {{aips_axes(0, 1).getValue(), aips_axes(1, 1).getValue(),
+        aips_axes(2, 1).getValue()}};
+    vector3r_t r = {{aips_axes(0, 2).getValue(), aips_axes(1, 2).getValue(),
+        aips_axes(2, 2).getValue()}};
+
+    AntennaField::CoordinateSystem system = {position, {p, q, r}};
+    return system;
+}
+
+void readAntennae(const Table &table, unsigned int id,
+    const AntennaField::Ptr &field)
+{
+    ROArrayQuantColumn<Double> c_offset(table, "ELEMENT_OFFSET", "m");
+    ROArrayColumn<Bool> c_flag(table, "ELEMENT_FLAG");
+
+    // Read element offsets and flags.
+    Matrix<Quantity> aips_offset = c_offset(id);
+    assert(aips_offset.shape().isEqual(IPosition(2, 3, aips_offset.ncolumn())));
+
+    Matrix<Bool> aips_flag = c_flag(id);
+    assert(aips_flag.shape().isEqual(IPosition(2, 2, aips_offset.ncolumn())));
+
+    for(size_t i = 0; i < aips_offset.ncolumn(); ++i)
+    {
+        AntennaField::Antenna antenna;
+        antenna.position[0] = aips_offset(0, i).getValue();
+        antenna.position[1] = aips_offset(1, i).getValue();
+        antenna.position[2] = aips_offset(2, i).getValue();
+        antenna.enabled[0] = !aips_flag(0, i);
+        antenna.enabled[1] = !aips_flag(1, i);
+        field->addAntenna(antenna);
+    }
+}
+
+AntennaField::Ptr readAntennaField(const Table &table, unsigned int id)
+{
+    AntennaField::Ptr field;
+    AntennaField::CoordinateSystem system = readCoordinateSystem(table, id);
+
+    ROScalarColumn<String> c_name(table, "NAME");
+    const string &name = c_name(id);
+
+    if(name == "LBA")
+    {
+        DualDipoleAntenna::Ptr model(new DualDipoleAntenna());
+        field = AntennaField::Ptr(new AntennaFieldLBA(name, system, model));
+    }
+    else // HBA, HBA0, HBA1
+    {
+        TileAntenna::TileConfig config = readTileConfig(table, id);
+        transformToFieldCoordinates(config, system.axes);
+
+        TileAntenna::Ptr model(new TileAntenna(config));
+        field = AntennaField::Ptr(new AntennaFieldHBA(name, system, model));
+    }
+
+    readAntennae(table, id, field);
+    return field;
+}
+
+AntennaField::Ptr readAntennaFieldAartfaac(const Table &table, const string &ant_type,
+                                           unsigned int id)
+{
+    AntennaField::Ptr field;
+    AntennaField::CoordinateSystem system = readCoordinateSystemAartfaac(table, id);
+
+    if (ant_type == "LBA")
+    {
+        DualDipoleAntenna::Ptr model(new DualDipoleAntenna());
+        field = AntennaField::Ptr(new AntennaFieldLBA(ant_type, system, model));
+    }
+    else // HBA
+    {
+         // TODO: implement this
+         throw std::runtime_error("HBA for Aartfaac is not implemented yet.");
+    }
+
+    // Add only one antenna to the field (no offset, always enabled)
+    AntennaField::Antenna antenna;
+    antenna.position[0] = 0.;
+    antenna.position[1] = 0.;
+    antenna.position[2] = 0.;
+    antenna.enabled[0] = true;
+    antenna.enabled[1] = true;
+
+    field->addAntenna(antenna);
+
+    return field;
+}
+
+void readStationPhaseReference(const Table &table, unsigned int id,
+    const Station::Ptr &station)
+{
+    const string columnName("LOFAR_PHASE_REFERENCE");
+    if(hasColumn(table, columnName))
+    {
+        ROScalarMeasColumn<MPosition> c_reference(table, columnName);
+        MPosition mReference = MPosition::Convert(c_reference(id),
+            MPosition::ITRF)();
+        MVPosition mvReference = mReference.getValue();
+        vector3r_t reference = {{mvReference(0), mvReference(1),
+            mvReference(2)}};
+
+        station->setPhaseReference(reference);
+    }
+}
+
+Station::Ptr readStation(const MeasurementSet &ms, unsigned int id)
+{
+    ROMSAntennaColumns antenna(ms.antenna());
+    assert(antenna.nrow() > id && !antenna.flagRow()(id));
+
+    // Get station name.
+    const string name(antenna.name()(id));
+
+    // Get station position (ITRF).
+    MPosition mPosition = MPosition::Convert(antenna.positionMeas()(id),
+        MPosition::ITRF)();
+    MVPosition mvPosition = mPosition.getValue();
+    const vector3r_t position = {{mvPosition(0), mvPosition(1), mvPosition(2)}};
+
+    // Create station.
+    Station::Ptr station(new Station(name, position));
+
+    // Read phase reference position (if available).
+    readStationPhaseReference(ms.antenna(), id, station);
+
+    // Read antenna field information.
+    ROScalarColumn<String> telescope_name_col(getSubTable(ms, "OBSERVATION"),
+                                              "TELESCOPE_NAME");
+    string telescope_name = telescope_name_col(0);
+
+    if (telescope_name == "LOFAR")
+    {
+        Table tab_field = getSubTable(ms, "LOFAR_ANTENNA_FIELD");
+        tab_field = tab_field(tab_field.col("ANTENNA_ID") == static_cast<Int>(id));
+
+        for(size_t i = 0; i < tab_field.nrow(); ++i)
+        {
+            station->addField(readAntennaField(tab_field, i));
+        }
+    }
+    else if (telescope_name == "AARTFAAC")
+    {
+        ROScalarColumn<String> ant_type_col(getSubTable(ms, "OBSERVATION"),
+                                            "AARTFAAC_ANTENNA_TYPE");
+        string ant_type = ant_type_col(0);
+
+        Table tab_field = getSubTable(ms, "ANTENNA");
+        station -> addField(readAntennaFieldAartfaac(tab_field, ant_type, id));
+    }
+
+    return station;
+}
+
+MDirection readTileBeamDirection(const casacore::MeasurementSet &ms) {
+    MDirection tileBeamDir;
+
+    Table fieldTable = getSubTable(ms, "FIELD");
+
+    if (fieldTable.nrow() != 1) {
+        throw std::runtime_error("MS has multiple fields, this does not work with the LOFAR beam library.");
+    }
+
+    if (hasColumn(fieldTable, "LOFAR_TILE_BEAM_DIR"))
+    {
+        ROArrayMeasColumn<MDirection> tileBeamCol(fieldTable,
+                                                  "LOFAR_TILE_BEAM_DIR");
+        tileBeamDir = *(tileBeamCol(0).data());
+    }
+    else
+    {
+      ROArrayMeasColumn<MDirection> tileBeamCol(fieldTable,
+                                                "DELAY_DIR");
+      tileBeamDir = *(tileBeamCol(0).data());
+    }
+
+    return tileBeamDir;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/MathUtil.cc b/CEP/Calibration/StationResponse/src/MathUtil.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1ce4c5af2ee7bf1169d138edacbd2beb6657c881
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/MathUtil.cc
@@ -0,0 +1,32 @@
+//# MathUtil.cc: Various mathematical operations on vectors and matrices.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/MathUtil.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/Station.cc b/CEP/Calibration/StationResponse/src/Station.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7c4e21045a43e83bd37767c818b8f980fcd93c5c
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/Station.cc
@@ -0,0 +1,176 @@
+//# Station.cc: Representation of the station beam former.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/Station.h>
+#include <StationResponse/MathUtil.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+Station::Station(const string &name, const vector3r_t &position)
+    :   itsName(name),
+        itsPosition(position),
+        itsPhaseReference(position)
+{
+}
+
+const string &Station::name() const
+{
+    return itsName;
+}
+
+const vector3r_t &Station::position() const
+{
+    return itsPosition;
+}
+
+void Station::setPhaseReference(const vector3r_t &reference)
+{
+    itsPhaseReference = reference;
+}
+
+const vector3r_t &Station::phaseReference() const
+{
+    return itsPhaseReference;
+}
+
+void Station::addField(const AntennaField::ConstPtr &field)
+{
+    itsFields.push_back(field);
+}
+
+size_t Station::nFields() const
+{
+    return itsFields.size();
+}
+
+AntennaField::ConstPtr Station::field(size_t i) const
+{
+    return (i < itsFields.size() ? itsFields[i] : AntennaField::ConstPtr());
+}
+
+Station::FieldList::const_iterator Station::beginFields() const
+{
+    return itsFields.begin();
+}
+
+Station::FieldList::const_iterator Station::endFields() const
+{
+    return itsFields.end();
+}
+
+matrix22c_t Station::response(real_t time, real_t freq,
+    const vector3r_t &direction, real_t freq0, const vector3r_t &station0,
+    const vector3r_t &tile0) const
+{
+    raw_response_t result = {{{{{}}, {{}}}}, {{}}};
+    for(FieldList::const_iterator field_it = beginFields(),
+        field_end = endFields(); field_it != field_end; ++field_it)
+    {
+        raw_array_factor_t field = fieldArrayFactor(*field_it, time, freq,
+            direction, freq0, phaseReference(), station0);
+
+        raw_response_t antenna = (*field_it)->rawResponse(time, freq,
+            direction, tile0);
+
+        result.response[0][0] += field.factor[0] * antenna.response[0][0];
+        result.response[0][1] += field.factor[0] * antenna.response[0][1];
+        result.response[1][0] += field.factor[1] * antenna.response[1][0];
+        result.response[1][1] += field.factor[1] * antenna.response[1][1];
+
+        result.weight[0] += field.weight[0] * antenna.weight[0];
+        result.weight[1] += field.weight[1] * antenna.weight[1];
+    }
+
+    return normalize(result);
+}
+
+diag22c_t Station::arrayFactor(real_t time, real_t freq,
+    const vector3r_t &direction, real_t freq0, const vector3r_t &station0,
+    const vector3r_t &tile0) const
+{
+    raw_array_factor_t af = {{{}}, {{}}};
+    for(FieldList::const_iterator field_it = beginFields(),
+        field_end = endFields(); field_it != field_end; ++field_it)
+    {
+        raw_array_factor_t field = fieldArrayFactor(*field_it, time, freq,
+            direction, freq0, phaseReference(), station0);
+
+        raw_array_factor_t antenna = (*field_it)->rawArrayFactor(time, freq,
+            direction, tile0);
+
+        af.factor[0] += field.factor[0] * antenna.factor[0];
+        af.factor[1] += field.factor[1] * antenna.factor[1];
+        af.weight[0] += field.weight[0] * antenna.weight[0];
+        af.weight[1] += field.weight[1] * antenna.weight[1];
+    }
+
+    return normalize(af);
+}
+
+raw_array_factor_t
+Station::fieldArrayFactor(const AntennaField::ConstPtr &field,
+    real_t, real_t freq, const vector3r_t &direction, real_t freq0,
+    const vector3r_t &position0, const vector3r_t &direction0) const
+{
+    real_t k = Constants::_2pi * freq / Constants::c;
+    real_t k0 = Constants::_2pi * freq0 / Constants::c;
+
+    vector3r_t offset = field->position() - position0;
+
+    raw_array_factor_t af = {{{}}, {{}}};
+    typedef AntennaField::AntennaList AntennaList;
+    for(AntennaList::const_iterator antenna_it = field->beginAntennae(),
+        antenna_end = field->endAntennae(); antenna_it != antenna_end;
+        ++antenna_it)
+    {
+        if(!antenna_it->enabled[0] && !antenna_it->enabled[1])
+        {
+            continue;
+        }
+
+        vector3r_t position = offset + antenna_it->position;
+        real_t phase = k * dot(position, direction) - k0 * dot(position,
+            direction0);
+        complex_t shift = complex_t(cos(phase), sin(phase));
+
+        if(antenna_it->enabled[0])
+        {
+            af.factor[0] += shift;
+            ++af.weight[0];
+        }
+
+        if(antenna_it->enabled[1])
+        {
+            af.factor[1] += shift;
+            ++af.weight[1];
+        }
+    }
+
+    return af;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/TileAntenna.cc b/CEP/Calibration/StationResponse/src/TileAntenna.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2813563ec96cad64914c102168e2470aef1854b6
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/TileAntenna.cc
@@ -0,0 +1,100 @@
+//# TileAntenna.cc: Semi-analytical model of a LOFAR HBA tile.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/TileAntenna.h>
+#include <StationResponse/Constants.h>
+#include <StationResponse/MathUtil.h>
+#include <ElementResponse/ElementResponse.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+TileAntenna::TileAntenna(const TileConfig &config)
+    :   itsConfig(config)
+{
+}
+
+void TileAntenna::setConfig(const TileConfig &config)
+{
+    itsConfig = config;
+}
+
+const TileAntenna::TileConfig &TileAntenna::config() const
+{
+    return itsConfig;
+}
+
+raw_array_factor_t TileAntenna::rawArrayFactor(real_t freq,
+    const vector3r_t &direction, const vector3r_t &direction0) const
+{
+    // Angular wave number.
+    real_t k = Constants::_2pi * freq / Constants::c;
+
+    // We need to compute the phase difference between a signal arriving from
+    // the target direction and a signal arriving from the reference direction,
+    // both relative to the center of the tile.
+    //
+    // This phase difference can be computed using a single dot product per
+    // dipole element by exploiting the fact that the dot product is
+    // distributive over vector addition:
+    //
+    // a . b + a . c = a . (b + c)
+    //
+    vector3r_t difference = direction - direction0;
+
+    complex_t af(0.0, 0.0);
+    for(TileConfig::const_iterator element_it = itsConfig.begin(),
+        element_end = itsConfig.end(); element_it != element_end; ++element_it)
+    {
+        // Compute the effective delay for a plane wave approaching from the
+        // direction of interest with respect to the position of element i
+        // when beam forming in the reference direction using time delays.
+        real_t shift = k * dot(difference, *element_it);
+        af += complex_t(cos(shift), sin(shift));
+    }
+
+    real_t size = itsConfig.size();
+    raw_array_factor_t result = {{{af, af}}, {{size, size}}};
+    return result;
+}
+
+matrix22c_t TileAntenna::elementResponse(real_t freq,
+    const vector3r_t &direction) const
+{
+    // The positive X dipole direction is SW of the reference orientation,
+    // which translates to a phi coordinate of 5/4*pi in the topocentric
+    // spherical coordinate system. The phi coordinate is corrected for this
+    // offset before evaluating the antenna model.
+    vector2r_t thetaphi = cart2thetaphi(direction);
+    thetaphi[1] -= 5.0 * Constants::pi_4;
+
+    matrix22c_t response;
+    element_response_hba(freq, thetaphi[0], thetaphi[1],
+        reinterpret_cast<std::complex<double> (&)[2][2]>(response));
+    return response;
+}
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/Types.cc b/CEP/Calibration/StationResponse/src/Types.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5267ae75691117365e166f513d915a879f48ee8a
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/Types.cc
@@ -0,0 +1,32 @@
+//# Types.cc: Declaration of types used in this library.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+#include <StationResponse/Types.h>
+
+namespace LOFAR
+{
+namespace StationResponse
+{
+
+} //# namespace StationResponse
+} //# namespace LOFAR
diff --git a/CEP/Calibration/StationResponse/src/makeresponseimage.cc b/CEP/Calibration/StationResponse/src/makeresponseimage.cc
new file mode 100644
index 0000000000000000000000000000000000000000..36a4276ffc95ecc460f1433b4a1f86000c1f80a3
--- /dev/null
+++ b/CEP/Calibration/StationResponse/src/makeresponseimage.cc
@@ -0,0 +1,636 @@
+//# makeresponseimage.cc: Generate images of the station response for a given
+//# MeasurementSet.
+//#
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+
+#include <StationResponse/Package__Version.h>
+#include <StationResponse/LofarMetaDataUtil.h>
+#include <Common/InputParSet.h>
+#include <Common/lofar_sstream.h>
+#include <Common/LofarLogger.h>
+#include <Common/SystemUtil.h>
+#include <Common/Version.h>
+#include <casacore/coordinates/Coordinates/CoordinateSystem.h>
+#include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
+#include <casacore/coordinates/Coordinates/StokesCoordinate.h>
+#include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
+#include <casacore/images/Images/PagedImage.h>
+#include <casacore/measures/Measures/MCDirection.h>
+#include <casacore/measures/Measures/MCPosition.h>
+#include <casacore/measures/Measures/MDirection.h>
+#include <casacore/measures/Measures/MeasConvert.h>
+#include <casacore/measures/Measures/MeasTable.h>
+#include <casacore/measures/Measures/MEpoch.h>
+#include <casacore/measures/Measures/MPosition.h>
+#include <casacore/ms/MeasurementSets/MeasurementSet.h>
+#include <casacore/ms/MeasurementSets/MSDataDescription.h>
+#include <casacore/ms/MeasurementSets/MSDataDescColumns.h>
+#include <casacore/ms/MeasurementSets/MSField.h>
+#include <casacore/ms/MeasurementSets/MSFieldColumns.h>
+#include <casacore/ms/MeasurementSets/MSObservation.h>
+#include <casacore/ms/MeasurementSets/MSObsColumns.h>
+#include <casacore/ms/MeasurementSets/MSSpectralWindow.h>
+#include <casacore/ms/MeasurementSets/MSSpWindowColumns.h>
+#include <casacore/tables/TaQL/ExprNode.h>
+
+// There is no wrapped include file lofar_iterator.h.
+#include <iterator>
+
+using namespace casacore;
+using namespace LOFAR;
+using namespace LOFAR::StationResponse;
+using LOFAR::operator<<;
+
+namespace
+{
+    /*!
+     *  \brief Map of ITRF directions required to compute an image of the
+     *  station beam.
+     *
+     *  The station beam library uses the ITRF coordinate system to express
+     *  station positions and source directions. Since the Earth moves with
+     *  respect to the sky, the ITRF coordinates of a source vary with time.
+     *  This structure stores the ITRF coordinates for the station and tile beam
+     *  former reference directions, as well as for a grid of points on the sky,
+     *  along with the time for which these ITRF coordinates are valid.
+     */
+    struct ITRFDirectionMap
+    {
+        /*!
+         *  \brief The time for which this ITRF direction map is valid (MJD(UTC)
+         *  in seconds).
+         */
+        double_t            time0;
+
+        /*!
+         *  \brief Station beam former reference direction expressed in ITRF
+         *  coordinates.
+         */
+        vector3r_t          station0;
+
+        /*!
+         *  \brief Tile beam former reference direction expressed in ITRF
+         *  coordinates.
+         */
+        vector3r_t          tile0;
+
+        /*!
+         *  \brief ITRF coordinates for a grid of points on the sky.
+         */
+        Matrix<vector3r_t>  directions;
+    };
+
+    /*!
+     *  \brief Create an ITRFDirectionMap.
+     *
+     *  \param coordinates Sky coordinate system definition.
+     *  \param shape Number of points along the RA and DEC axis.
+     *  \param epoch Time for which to compute the ITRF coordinates.
+     *  \param position0 Station beam former reference position (phase
+     *  reference).
+     *  \param station0 Station beam former reference direction (pointing).
+     *  \param tile0 Tile beam former reference direction (pointing).
+     */
+    ITRFDirectionMap makeDirectionMap(const DirectionCoordinate &coordinates,
+        const IPosition &shape, const MEpoch &epoch, const MPosition &position0,
+        const MDirection &station0, const MDirection &tile0);
+
+    /*!
+     *  \brief Create a DirectionCoordinate instance that defines an image
+     *  coordinate system on the sky (J2000).
+     *
+     *  \param reference Direction that corresponds to the origin of the
+     *  coordinate system.
+     *  \param size Number of points along each axis (RA, DEC). The index of the
+     *  origin of the coordinate system is (size / 2, size / 2).
+     *  \param delta Angular step size in radians (assumed to be the same for
+     *  both axes).
+     */
+    DirectionCoordinate makeCoordinates(const MDirection &reference,
+        unsigned int size, double delta);
+
+    /*!
+     *  \brief Convert an ITRF position given as a StationResponse::vector3r_t
+     *  instance to a casacore::MPosition.
+     */
+    MPosition toMPositionITRF(const vector3r_t &position);
+
+    /*!
+     *  \brief Convert a casacore::MPosition instance to a
+     *  StationResponse::vector3r_t instance.
+     */
+    vector3r_t fromMPosition(const MPosition &position);
+
+    /*!
+     *  \brief Convert a casacore::MDirection instance to a
+     *  StationResponse::vector3r_t instance.
+     */
+    vector3r_t fromMDirection(const MDirection &direction);
+
+    /*!
+     *  \brief Check if the specified column exists as a column of the specified
+     *  table.
+     *
+     *  \param table The Table instance to check.
+     *  \param column The name of the column.
+     */
+    bool hasColumn(const Table &table, const string &column);
+
+    /*!
+     *  \brief Check if the specified sub-table exists as a sub-table of the
+     *  specified table.
+     *
+     *  \param table The Table instance to check.
+     *  \param name The name of the sub-table.
+     */
+    bool hasSubTable(const Table &table, const string &name);
+
+    /*!
+     *  \brief Provide access to a sub-table by name.
+     *
+     *  \param table The Table instance to which the sub-table is associated.
+     *  \param name The name of the sub-table.
+     */
+    Table getSubTable(const Table &table, const string &name);
+
+    /*!
+     *  \brief Attempt to read the position of the observatory. If the
+     *  observatory position is unknown, the specified default position is
+     *  returned.
+     *
+     *  \param ms MeasurementSet to read the observatory position from.
+     *  \param idObservation Identifier that determines of which observation the
+     *  observatory position should be read.
+     *  \param defaultPosition The position that will be returned if the
+     *  observatory position is unknown.
+     */
+    MPosition readObservatoryPosition(const MeasurementSet &ms,
+        unsigned int idObservation, const MPosition &defaultPosition);
+
+    /*!
+     *  \brief Read the list of unique timestamps.
+     *
+     *  \param ms MeasurementSet to read the list of unique timestamps from.
+     */
+    Vector<Double> readUniqueTimes(const MeasurementSet &ms);
+
+    /*!
+     *  \brief Read the reference frequency of the subband associated to the
+     *  specified data description identifier.
+     *
+     *  \param ms MeasurementSet to read the reference frequency from.
+     *  \param idDataDescription Identifier that determines of which subband the
+     *  reference frequency should be read.
+     */
+    double readFreqReference(const MeasurementSet &ms,
+        unsigned int idDataDescription);
+
+    /*!
+     *  \brief Read the phase reference direction.
+     *
+     *  \param ms MeasurementSet to read the phase reference direction from.
+     *  \param idField Identifier of the field of which the phase reference
+     *  direction should be read.
+     */
+    MDirection readPhaseReference(const MeasurementSet &ms,
+        unsigned int idField);
+
+    /*!
+     *  \brief Read the station beam former reference direction.
+     *
+     *  \param ms MeasurementSet to read the station beam former reference
+     *  direction from.
+     *  \param idField Identifier of the field of which the station beam former
+     *  reference direction should be read.
+     */
+    MDirection readDelayReference(const MeasurementSet &ms,
+        unsigned int idField);
+
+    /*!
+     *  \brief Read the station beam former reference direction.
+     *
+     *  \param ms MeasurementSet to read the tile beam former reference
+     *  direction from.
+     *  \param idField Identifier of the field of which the tile beam former
+     *  reference direction should be read.
+     */
+    MDirection readTileReference(const MeasurementSet &ms,
+        unsigned int idField);
+
+    /*!
+     *  \brief Store the specified cube of pixel data as a CASA image.
+     *
+     *  \param data Pixel data. The third dimension is assumed to be of length
+     *  4, referring to the correlation products XX, XY, YX, YY (in this order).
+     *  \param coordinates Sky coordinate system definition.
+     *  \param frequency Frequency for which the pixel data is valid (Hz).
+     *  \param name File name of the output image.
+     */
+    template <class T>
+    void store(const Cube<T> &data, const DirectionCoordinate &coordinates,
+        double frequency, const string &name);
+
+    /*!
+     *  \brief Convert a string to a CASA Quantity (value with unit).
+     */
+    Quantity readQuantity(const String &in);
+
+    /*!
+     *  \brief Remove all elements from the range [first, last) that fall
+     *  outside the interval [min, max].
+     *
+     *  This function returns an iterator new_last such that the range [first,
+     *  new_last) contains no elements that fall outside the interval [min,
+     *  max]. The iterators in the range [new_last, last) are all still
+     *  dereferenceable, but the elements that they point to are unspecified.
+     *  The order of the elements that are not removed is unchanged.
+     */
+    template <typename T>
+    T filter(T first, T last, int min, int max);
+} //# unnamed namespace
+
+
+int main(int argc, char *argv[])
+{
+    TEST_SHOW_VERSION(argc, argv, StationResponse);
+    INIT_LOGGER(basename(string(argv[0])));
+    Version::show<StationResponseVersion>(cout);
+
+    // Parse inputs.
+    LOFAR::InputParSet inputs;
+    inputs.create("ms", "", "Name of input MeasurementSet", "string");
+    inputs.create("stations", "0", "IDs of stations to process", "int vector");
+    inputs.create("cellsize", "60arcsec", "Angular pixel size",
+        "quantity string");
+    inputs.create("size", "256", "Number of pixels along each axis", "int");
+    inputs.create("offset", "0s", "Time offset from the start of the MS",
+        "quantity string");
+    inputs.create("frames", "0", "Number of images that will be generated for"
+        " each station (equally spaced over the duration of the MS)", "int");
+    inputs.create("abs", "false", "If set to true, store the absolute value of"
+        " the beam response instead of the complex value (intended for use with"
+        " older versions of casaviewer)", "bool");
+    inputs.readArguments(argc, argv);
+
+    vector<int> stationIDs(inputs.getIntVector("stations"));
+    Quantity cellsize = readQuantity(inputs.getString("cellsize"));
+    unsigned int size = max(inputs.getInt("size"), 1);
+    Quantity offset = readQuantity(inputs.getString("offset"));
+    unsigned int nFrames = max(inputs.getInt("frames"), 1);
+    Bool abs = inputs.getBool("abs");
+
+    // Open MS.
+    MeasurementSet ms(inputs.getString("ms"));
+    unsigned int idObservation = 0, idField = 0, idDataDescription = 0;
+
+    // Read station meta-data.
+    vector<Station::Ptr> stations;
+    readStations(ms, std::back_inserter(stations));
+
+    // Remove illegal station indices.
+    stationIDs.erase(filter(stationIDs.begin(), stationIDs.end(), 0,
+        static_cast<int>(stations.size()) - 1), stationIDs.end());
+
+    // Read unique timestamps
+    Table selection =
+        ms(ms.col("OBSERVATION_ID") == static_cast<Int>(idObservation)
+            && ms.col("FIELD_ID") == static_cast<Int>(idField)
+            && ms.col("DATA_DESC_ID") == static_cast<Int>(idDataDescription));
+    Vector<Double> time = readUniqueTimes(selection);
+
+    // Read reference frequency.
+    double refFrequency = readFreqReference(ms, idDataDescription);
+
+    // Use the position of the first selected station as the array reference
+    // position if the observatory position cannot be found.
+    MPosition refPosition = readObservatoryPosition(ms, idField,
+        toMPositionITRF(stations.front()->position()));
+
+    // Read phase reference direction.
+    MDirection refPhase = readPhaseReference(ms, idField);
+
+    // Read delay reference direction.
+    MDirection refDelay = readDelayReference(ms, idField);
+
+    // Read tile reference direction.
+    MDirection refTile = readTileReference(ms, idField);
+
+    // Create image coordinate system.
+    IPosition shape(2, size, size);
+    DirectionCoordinate coordinates = makeCoordinates(refPhase, size,
+        cellsize.getValue("rad"));
+
+    // Compute station response images.
+    Cube<Complex> response(size, size, 4);
+
+    MEpoch refEpoch;
+    Quantity refTime(time(0), "s");
+    refTime = refTime + offset;
+    Quantity deltaTime((time(time.size() - 1) - time(0) - offset.getValue("s"))
+        / (nFrames - 1), "s");
+
+    for(size_t j = 0; j < nFrames; ++j)
+    {
+        cout << "[ Frame: " << (j + 1) << "/" << nFrames << " Offset: +"
+            << refTime.getValue() - time(0) << " s ]" << endl;
+
+        // Update reference epoch.
+        refEpoch.set(refTime);
+
+        cout << "Creating ITRF direction map... " << flush;
+        ITRFDirectionMap directionMap = makeDirectionMap(coordinates, shape,
+            refEpoch, refPosition, refDelay, refTile);
+        cout << "done." << endl;
+
+        cout << "Computing response images... " << flush;
+        for(vector<int>::const_iterator it = stationIDs.begin(),
+            end = stationIDs.end(); it != end; ++it)
+        {
+            Station::ConstPtr station = stations[*it];
+            cout << *it << ":" << station->name() << " " << flush;
+
+            for(size_t y = 0; y < size; ++y)
+            {
+                for(size_t x = 0; x < size; ++x)
+                {
+                    matrix22c_t E = station->response(directionMap.time0,
+                        refFrequency, directionMap.directions(x, y),
+                        refFrequency, directionMap.station0,
+                        directionMap.tile0);
+
+                    response(x, y, 0) = E[0][0];
+                    response(x, y, 1) = E[0][1];
+                    response(x, y, 2) = E[1][0];
+                    response(x, y, 3) = E[1][1];
+                }
+            }
+
+            std::ostringstream oss;
+            oss << "response-" << station->name() << "-frame-" << (j + 1)
+                << ".img";
+
+            if(abs)
+            {
+                store(Cube<Float>(amplitude(response)), coordinates,
+                    refFrequency, oss.str());
+            }
+            else
+            {
+                store(response, coordinates, refFrequency, oss.str());
+            }
+        }
+        cout << endl;
+
+        refTime = refTime + deltaTime;
+    }
+
+    return 0;
+}
+
+namespace
+{
+    ITRFDirectionMap makeDirectionMap(const DirectionCoordinate &coordinates,
+        const IPosition &shape, const MEpoch &epoch, const MPosition &position0,
+        const MDirection &station0, const MDirection &tile0)
+    {
+        ITRFDirectionMap map;
+
+        // Convert from MEpoch to a time in MJD(UTC) in seconds.
+        MEpoch mEpochUTC = MEpoch::Convert(epoch, MEpoch::Ref(MEpoch::UTC))();
+        MVEpoch mvEpochUTC = mEpochUTC.getValue();
+        Quantity qEpochUTC = mvEpochUTC.getTime();
+        map.time0 = qEpochUTC.getValue("s");
+
+        // Create conversion engine J2000 => ITRF at the specified epoch.
+        MDirection::Convert convertor = MDirection::Convert(MDirection::J2000,
+          MDirection::Ref(MDirection::ITRF, MeasFrame(epoch, position0)));
+
+        // Compute station and tile beam former reference directions in ITRF at
+        // the specified epoch.
+        map.station0 = fromMDirection(convertor(station0));
+        map.tile0 = fromMDirection(convertor(tile0));
+
+        // Pre-allocate space for the grid of ITRF directions.
+        map.directions.resize(shape);
+
+        // Compute ITRF directions.
+        MDirection world;
+        Vector<Double> pixel = coordinates.referencePixel();
+        for(pixel(1) = 0.0; pixel(1) < shape(1); ++pixel(1))
+        {
+            for(pixel(0) = 0.0; pixel(0) < shape(0); ++pixel(0))
+            {
+                // CoordinateSystem::toWorld(): RA range [-pi,pi], DEC range
+                // [-pi/2,pi/2].
+                if(coordinates.toWorld(world, pixel))
+                {
+                    map.directions(pixel(0), pixel(1)) =
+                        fromMDirection(convertor(world));
+                }
+            }
+        }
+
+        return map;
+    }
+
+    DirectionCoordinate makeCoordinates(const MDirection &reference,
+        unsigned int size, double delta)
+    {
+        MDirection referenceJ2000 = MDirection::Convert(reference,
+            MDirection::J2000)();
+        Quantum<Vector<Double> > referenceAngles = referenceJ2000.getAngle();
+        double ra = referenceAngles.getBaseValue()(0);
+        double dec = referenceAngles.getBaseValue()(1);
+
+        Matrix<Double> xform(2,2);
+        xform = 0.0; xform.diagonal() = 1.0;
+        return DirectionCoordinate(MDirection::J2000,
+            Projection(Projection::SIN), ra, dec, -delta, delta, xform,
+            size / 2, size / 2);
+    }
+
+    MPosition toMPositionITRF(const vector3r_t &position)
+    {
+        MVPosition mvITRF(position[0], position[1], position[2]);
+        return MPosition(mvITRF, MPosition::ITRF);
+    }
+
+    vector3r_t fromMPosition(const MPosition &position)
+    {
+        MVPosition mvPosition = position.getValue();
+        vector3r_t result = {{mvPosition(0), mvPosition(1), mvPosition(2)}};
+        return result;
+    }
+
+    vector3r_t fromMDirection(const MDirection &direction)
+    {
+      MVDirection mvDirection = direction.getValue();
+      vector3r_t result = {{mvDirection(0), mvDirection(1), mvDirection(2)}};
+      return result;
+    }
+
+    bool hasColumn(const Table &table, const string &column)
+    {
+        return table.tableDesc().isColumn(column);
+    }
+
+    bool hasSubTable(const Table &table, const string &name)
+    {
+        return table.keywordSet().isDefined(name);
+    }
+
+    Table getSubTable(const Table &table, const string &name)
+    {
+        return table.keywordSet().asTable(name);
+    }
+
+    MPosition readObservatoryPosition(const MeasurementSet &ms,
+        unsigned int idObservation, const MPosition &defaultPosition)
+    {
+        // Get the instrument position in ITRF coordinates, or use the centroid
+        // of the station positions if the instrument position is unknown.
+        ROMSObservationColumns observation(ms.observation());
+        ASSERT(observation.nrow() > idObservation);
+        ASSERT(!observation.flagRow()(idObservation));
+
+        // Read observatory name and try to look-up its position.
+        const string observatory = observation.telescopeName()(idObservation);
+
+        // Look-up observatory position, default to specified default position.
+        MPosition position(defaultPosition);
+        MeasTable::Observatory(position, observatory);
+        return position;
+    }
+
+    Vector<Double> readUniqueTimes(const MeasurementSet &ms)
+    {
+        Table tab_sorted = ms.sort("TIME", Sort::Ascending, Sort::HeapSort
+            | Sort::NoDuplicates);
+
+        ROScalarColumn<Double> c_time(tab_sorted, "TIME");
+        return c_time.getColumn();
+    }
+
+    double readFreqReference(const MeasurementSet &ms,
+        unsigned int idDataDescription)
+    {
+        ROMSDataDescColumns desc(ms.dataDescription());
+        ASSERT(desc.nrow() > idDataDescription);
+        ASSERT(!desc.flagRow()(idDataDescription));
+        uInt idWindow = desc.spectralWindowId()(idDataDescription);
+
+        ROMSSpWindowColumns window(ms.spectralWindow());
+        ASSERT(window.nrow() > idWindow);
+        ASSERT(!window.flagRow()(idWindow));
+
+        return window.refFrequency()(idWindow);
+    }
+
+    MDirection readPhaseReference(const MeasurementSet &ms,
+        unsigned int idField)
+    {
+        ROMSFieldColumns field(ms.field());
+        ASSERT(field.nrow() > idField);
+        ASSERT(!field.flagRow()(idField));
+
+        return field.phaseDirMeas(idField);
+    }
+
+    MDirection readDelayReference(const MeasurementSet &ms,
+        unsigned int idField)
+    {
+        ROMSFieldColumns field(ms.field());
+        ASSERT(field.nrow() > idField);
+        ASSERT(!field.flagRow()(idField));
+
+        return field.delayDirMeas(idField);
+    }
+
+    MDirection readTileReference(const MeasurementSet &ms, unsigned int idField)
+    {
+        // The MeasurementSet class does not support LOFAR specific columns, so
+        // we use ROArrayMeasColumn to read the tile beam reference direction.
+        Table tab_field = getSubTable(ms, "FIELD");
+
+        static const String columnName = "LOFAR_TILE_BEAM_DIR";
+        if(hasColumn(tab_field, columnName))
+        {
+            ROArrayMeasColumn<MDirection> c_direction(tab_field, columnName);
+            if(c_direction.isDefined(idField))
+            {
+                return c_direction(idField)(IPosition(1, 0));
+            }
+        }
+
+        // By default, the tile beam reference direction is assumed to be equal
+        // to the station beam reference direction (for backward compatibility,
+        // and for non-HBA measurements).
+        return readDelayReference(ms, idField);
+    }
+
+    template <class T>
+    void store(const Cube<T> &data, const DirectionCoordinate &coordinates,
+        double frequency, const string &name)
+    {
+        ASSERT(data.shape()(2) == 4);
+
+        Vector<Int> stokes(4);
+        stokes(0) = Stokes::XX;
+        stokes(1) = Stokes::XY;
+        stokes(2) = Stokes::YX;
+        stokes(3) = Stokes::YY;
+
+        CoordinateSystem csys;
+        csys.addCoordinate(coordinates);
+        csys.addCoordinate(StokesCoordinate(stokes));
+        csys.addCoordinate(SpectralCoordinate(MFrequency::TOPO, frequency, 0.0,
+            0.0));
+
+        PagedImage<T> im(TiledShape(IPosition(4, data.shape()(0),
+            data.shape()(1), 4, 1)), csys, name);
+        im.putSlice(data, IPosition(4, 0, 0, 0, 0));
+    }
+
+    Quantity readQuantity(const String &in)
+    {
+        Quantity result;
+        bool status = Quantity::read(result, in);
+        ASSERT(status);
+        return result;
+    }
+
+    template <typename T>
+    T filter(T first, T last, int min, int max)
+    {
+        T new_last = first;
+        for(; first != last; ++first)
+        {
+            if(*first >= min && *first <= max)
+            {
+                *new_last++ = *first;
+            }
+        }
+
+        return new_last;
+    }
+} // unnamed namespace.
diff --git a/CEP/Calibration/pystationresponse/CMakeLists.txt b/CEP/Calibration/pystationresponse/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e7c432c3b586d385eb41e00de6ebc164cdc3416f
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/CMakeLists.txt
@@ -0,0 +1,11 @@
+# $Id$
+
+lofar_package(pystationresponse 1.0 DEPENDS StationResponse)
+
+include(LofarFindPackage)
+lofar_find_package(Python 3.4 REQUIRED)
+lofar_find_package(Boost REQUIRED COMPONENTS python3)
+lofar_find_package(Casacore REQUIRED COMPONENTS python)
+
+add_subdirectory(src)
+add_subdirectory(test)
diff --git a/CEP/Calibration/pystationresponse/src/CMakeLists.txt b/CEP/Calibration/pystationresponse/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0d7aa5176a1ef06464b012765ae4dab74a1193d7
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/src/CMakeLists.txt
@@ -0,0 +1,28 @@
+# $Id$
+
+include(LofarPackageVersion)
+
+# Add current build directory to the include path. This is needed, because
+# pystationresponse.cc #include's Package__Version.cc (yucky!).
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+lofar_add_library(_stationresponse MODULE pystationresponse.cc)
+set_target_properties(_stationresponse PROPERTIES 
+  PREFIX ""
+  LIBRARY_OUTPUT_DIRECTORY ${PYTHON_BUILD_DIR}/lofar/stationresponse)
+
+# This is a quick-and-dirty fix to install the Python binding module in the
+# right place. It will now be installed twice, because lofar_add_library()
+# will install it in $prefix/$libdir
+install(TARGETS _stationresponse
+  DESTINATION ${PYTHON_INSTALL_DIR}/lofar/stationresponse)
+
+# Dummy library, needed because lofar_add_executable() takes its dependencies
+# from libraries added with lofar_add_library() (see bug #1430)
+lofar_add_library(lofar_pystationresponse Package__Version.cc)
+
+lofar_add_bin_program(versionpystationresponse versionpystationresponse.cc)
+
+# Install Python modules
+include(PythonInstall)
+python_install(__init__.py DESTINATION lofar/stationresponse)
diff --git a/CEP/Calibration/pystationresponse/src/__init__.py b/CEP/Calibration/pystationresponse/src/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..87961234ba1774f50361024fa1c918deebf67484
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/src/__init__.py
@@ -0,0 +1,210 @@
+# __init__.py: Top level .py file for python stationresponse interface
+# Copyright (C) 2011
+# ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id$
+
+from ._stationresponse import StationResponse
+
+class stationresponse(object):
+    """
+    The Python interface to the LOFAR station beam model.
+    """
+
+    def __init__ (self, msname, inverse = False, useElementResponse = True,
+        useArrayFactor = True, useChanFreq = False):
+        """Create a stationresponse object that can be used to evaluate the
+        LOFAR beam for the given Measurement Set.
+
+        The Measurement Set defines the station and dipole positions, the phase
+        center, and the channel frequencies (and reference frequency) for which
+        the LOFAR beam will be evaluated.
+
+        `msname`
+          Name of the Measurement Set.
+        `inverse`
+          Compute the inverse of the LOFAR beam (default False).
+        `useElementResponse`
+          Include the effect of the dual dipole (element) beam (default True).
+        `useArrayFactor`
+          Include the effect of the station and tile array factor (default
+          True).
+        `useChanFreq`
+          Compute the phase shift for the station beamformer using the channel
+          frequency instead of the subband reference frequency. This option
+          should be enabled for Measurement Sets that contain multiple subbands
+          compressed to single channels inside a single spectral window
+          (default: False).
+
+        For example::
+
+        import pyrap.tables
+        import lofar.stationresponse
+        response = lofar.stationresponse.stationresponse('test.MS')
+
+        # Iterate over all time stamps in the Measurement Set and compute the
+        # beam Jones matrix for station 0, channel 0.
+        ms = pyrap.tables.table('test.MS')
+        for subtable in ms.iter('TIME'):
+            time = subtable.getcell("TIME", 0)
+            print time, response.evaluateChannel(time, 0, 0)
+        """
+        self._response = StationResponse(msname, inverse,
+          useElementResponse, useArrayFactor, useChanFreq)
+
+    def version (self, type='other'):
+        """Show the software version."""
+        return self._response.version (type)
+
+    def setRefDelay (self, ra, dec):
+        """Set the reference direction used by the station beamformer. By
+        default, DELAY_DIR of field 0 is used.
+
+        `ra`
+          Right ascension (in radians, J2000)
+        `dec`
+          Declination (in radians, J2000)
+        """
+        self._response.setRefDelay(ra, dec)
+
+    def getRefDelay (self, time):
+        """Get the reference direction used by the station beamformer.
+        Returns an ITRF vector in meters (numpy array of 3 floats).
+ 
+        `time`
+          Time at which to evaluate the direction
+        """
+        return self._response.getRefDelay(time)
+
+    def setRefTile (self, ra, dec):
+        """Set the reference direction used by the analog tile beamformer
+        (relevant for HBA observations only). By default, LOFAR_TILE_BEAM_DIR
+        of field 0 is used. If not present, DELAY_DIR of field 0 is used
+        instead.
+
+        `ra`
+          Right ascension (in radians, J2000)
+        `dec`
+          Declination (in radians, J2000)
+        """
+        self._response.setRefTile(ra, dec)
+
+    def getRefTile (self, time):
+        """Get the reference direction used by the analog tile beamformer
+        (relevant for HBA observations only).
+        Returns an ITRF vector in meters (numpy array of 3 floats).
+ 
+        `time`
+          Time at which to evaluate the direction
+        """
+        return self._response.getRefTile(time)
+
+    def setDirection (self, ra, dec):
+        """Set the direction of interest (can be and often will be different
+        from the pointing). By default, PHASE_DIR of field 0 is used.
+
+        `ra`
+          Right ascension (in radians, J2000)
+        `dec`
+          Declination (in radians, J2000)
+        """
+        self._response.setDirection(ra, dec)
+
+    def getDirection (self, time):
+        """Get the direction of interest.
+        Returns an ITRF vector in meters (numpy array of 3 floats).
+
+        `time`
+          Time at which to evaluate the direction
+        """
+        return self._response.getDirection(time)
+
+    def evaluate (self, time):
+        """Compute the beam Jones matrix for all stations and channels at the
+        given time. The result is returned as a 4-dim complex numpy array with
+        shape: no. of stations x no. of channels x 2 x 2.
+
+        `time`
+          Time (MJD in seconds)
+        """
+        return self._response.evaluate0(time)
+
+    def evaluateStation (self, time, station):
+        """Compute the beam Jones matrix for all channels at the given time for
+        the given station. The result is returned as a 3-dim complex numpy array
+        with shape: no. of channels x 2 x 2.
+
+        `time`
+          Time (MJD in seconds).
+        `station`
+          Station number (as in the ANTENNA table of the Measurement Set).
+        """
+        return self._response.evaluate1(time, station)
+
+    def evaluateChannel (self, time, station, channel):
+        """Compute the beam Jones matrix for the given time, station, and
+        channel. The result is returned as a 2-dim complex numpy array with
+        shape: 2 x 2.
+
+        `time`
+          Time (MJD in seconds).
+        `station`
+          Station number (as defined in the ANTENNA table of the Measurement
+          Set).
+        `channel`
+          Channel number (as in the SPECTRAL_WINDOW table of the Measurement
+          Set).
+        """
+        return self._response.evaluate2(time, station, channel)
+    
+    def evaluateFreq (self, time, station, freq):
+        """Compute the beam Jones matrix for the given time, station, and
+        frequency. The result is returned as a 2-dim complex numpy array with
+        shape: 2 x 2.
+
+        `time`
+          Time (MJD in seconds).
+        `station`
+          Station number (as defined in the ANTENNA table of the Measurement
+          Set).
+        `frequency`
+          Frequency to compute beam at (in Hz)
+        """
+        return self._response.evaluate3(time, station, freq)
+
+    def evaluateFreqITRF (self, time, station, freq, direction, station0, tile0):
+        """Compute the beam Jones matrix for the given time, station, and
+        frequency, with the given ITRF directions.
+        The result is returned as a 2-dim complex numpy array with
+        shape: 2 x 2.
+
+        `time`
+          Time (MJD in seconds).
+        `station`
+          Station number (as defined in the ANTENNA table of the Measurement
+          Set).
+        `frequency`
+          Frequency to compute beam at (in Hz)
+        `direction`
+          ITRF direction to compute beam at (numpy array with 3 floats)
+        `station0`
+          ITRF direction of the station beamformer (numpy array with 3 floats)
+        `tile0`
+          ITRF direction of the tile beamformer (numpy array with 3 floats)
+        """
+        return self._response.evaluate4(time, station, freq, direction, station0, tile0)
diff --git a/CEP/Calibration/pystationresponse/src/pystationresponse.cc b/CEP/Calibration/pystationresponse/src/pystationresponse.cc
new file mode 100755
index 0000000000000000000000000000000000000000..828098ec170792e9841f754b17d9ff4751bf5b6e
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/src/pystationresponse.cc
@@ -0,0 +1,769 @@
+//# pystationresponse.cc: python module for StationResponse object.
+//# Copyright (C) 2007
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite is free software: you can redistribute it and/or
+//# modify it under the terms of the GNU General Public License as published
+//# by the Free Software Foundation, either version 3 of the License, or
+//# (at your option) any later version.
+//#
+//# The LOFAR software suite is distributed in the hope that it will be useful,
+//# but WITHOUT ANY WARRANTY; without even the implied warranty of
+//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//# GNU General Public License for more details.
+//#
+//# You should have received a copy of the GNU General Public License along
+//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id$
+
+#include <lofar_config.h>
+
+#include <Common/LofarLogger.h>
+
+#include <StationResponse/ITRFDirection.h>
+#include <StationResponse/LofarMetaDataUtil.h>
+#include <StationResponse/Station.h>
+
+#include <casacore/casa/Arrays/Array.h>
+#include <casacore/casa/Arrays/Matrix.h>
+#include <casacore/casa/Arrays/Cube.h>
+#include <casacore/casa/Containers/ValueHolder.h>
+#include <casacore/ms/MeasurementSets/MeasurementSet.h>
+#include <casacore/ms/MeasurementSets/MSColumns.h>
+#include <casacore/measures/Measures/MeasTable.h>
+#include <casacore/measures/Measures/MPosition.h>
+#include <casacore/measures/Measures/MDirection.h>
+#include <casacore/measures/Measures/MeasConvert.h>
+#include <casacore/measures/Measures/MCPosition.h>
+#include <casacore/measures/Measures/MCDirection.h>
+
+#if defined(HAVE_CASACORE)
+#include <casacore/python/Converters/PycExcp.h>
+#include <casacore/python/Converters/PycBasicData.h>
+#include <casacore/python/Converters/PycValueHolder.h>
+#define pyrap python
+#else
+#include <pyrap/Converters/PycExcp.h>
+#include <pyrap/Converters/PycBasicData.h>
+#include <pyrap/Converters/PycValueHolder.h>
+#endif
+
+#include <boost/python.hpp>
+#include <boost/python/args.hpp>
+
+#include "Package__Version.cc"
+
+using namespace casacore;
+using namespace boost::python;
+using namespace LOFAR::StationResponse;
+
+namespace LOFAR
+{
+namespace BBS
+{
+  namespace
+  {
+    /*!
+     *  \brief Convert an ITRF position given as a StationResponse::vector3r_t
+     *  instance to a casacore::MPosition.
+     */
+    MPosition toMPositionITRF(const vector3r_t &position);
+
+    /*!
+     *  \brief Convert a casacore::MPosition instance to a
+     #  StationResponse::vector3r_t instance.
+     */
+    vector3r_t fromMPosition(const MPosition &position);
+
+    /*!
+     *  \brief Convert a casacore::MDirection instance to a
+     *  StationResponse::vector3r_t instance.
+     */
+    vector3r_t fromMDirection(const MDirection &direction);
+
+    /*!
+    *  \brief Check if the specified column exists as a column of the specified
+    *  table.
+    *
+    *  \param table The Table instance to check.
+    *  \param column The name of the column.
+    */
+    bool hasColumn(const Table &table, const string &column);
+
+    /*!
+    *  \brief Check if the specified sub-table exists as a sub-table of the
+    *  specified table.
+    *
+    *  \param table The Table instance to check.
+    *  \param name The name of the sub-table.
+    */
+    bool hasSubTable(const Table &table, const string &name);
+
+    /*!
+    *  \brief Provide access to a sub-table by name.
+    *
+    *  \param table The Table instance to which the sub-table is associated.
+    *  \param name The name of the sub-table.
+    */
+    Table getSubTable(const Table &table, const string &name);
+
+    /*!
+    *  \brief Attempt to read the position of the observatory. If the
+    *  observatory position is unknown, the specified default position is
+    *  returned.
+    *
+    *  \param ms MeasurementSet to read the observatory position from.
+    *  \param idObservation Identifier that determines of which observation the
+    *  observatory position should be read.
+    *  \param defaultPosition The position that will be returned if the
+    *  observatory position is unknown.
+    */
+    MPosition readObservatoryPosition(const MeasurementSet &ms,
+      unsigned int idObservation, const MPosition &defaultPosition);
+
+    /*!
+     *  \brief Read the phase reference direction.
+     *
+     *  \param ms MeasurementSet to read the phase reference direction from.
+     *  \param idField Identifier of the field of which the phase reference
+     *  direction should be read.
+     */
+    MDirection readPhaseReference(const MeasurementSet &ms,
+      unsigned int idField);
+
+    /*!
+    *  \brief Read the station beam former reference direction.
+    *
+    *  \param ms MeasurementSet to read the station beam former reference
+    *  direction from.
+    *  \param idField Identifier of the field of which the station beam former
+    *  reference direction should be read.
+    */
+    MDirection readDelayReference(const MeasurementSet &ms,
+      unsigned int idField);
+
+    /*!
+    *  \brief Read the station beam former reference direction.
+    *
+    *  \param ms MeasurementSet to read the tile beam former reference direction
+    *  from.
+    *  \param idField Identifier of the field of which the tile beam former
+    *  reference direction should be read.
+    */
+    MDirection readTileReference(const MeasurementSet &ms,
+      unsigned int idField);
+  } //# unnamed namespace
+
+  class PyStationResponse
+  {
+  public:
+    PyStationResponse(const string& msName, bool inverse = false,
+        bool useElementResponse = true, bool useArrayFactor = true,
+        bool useChanFreq = false);
+
+    // Get the software version.
+    string version(const string& type) const;
+
+    // Set the delay reference direction in radians, J2000. The delay reference
+    // direction is the direction used by the station beamformer.
+    void setRefDelay(double ra, double dec);
+
+    // Get the delay reference direction in meters, ITRF. The delay reference
+    // direction is the direction used by the station beamformer.
+    ValueHolder getRefDelay(real_t time);
+
+    // Set the tile reference direction in radians, J2000. The tile reference
+    // direction is the direction used by the analog tile beamformer and is
+    // relevant only for HBA observations.
+    void setRefTile(double ra, double dec);
+
+    // Get the tile reference direction in meters, ITRF. The delay reference
+    // direction is the direction used by the analog tile beamformer and is
+    // relevant only for HBA observations.
+    ValueHolder getRefTile(real_t time);
+
+    // Set the direction of interest in radians, J2000. Can and often will be
+    // different than the delay and/or tile reference direction.
+    void setDirection(double ra, double dec);
+
+    // Get the direction of intereset in meters, ITRF.
+    ValueHolder getDirection(real_t time);
+
+    // Compute the LOFAR beam Jones matrices for the given time, station, and/or
+    // channel.
+    ValueHolder evaluate0(double time);
+    ValueHolder evaluate1(double time, int station);
+    ValueHolder evaluate2(double time, int station, int channel);
+    ValueHolder evaluate3(double time, int station, double freq);
+    ValueHolder evaluate4(double time, int station, double freq, const ValueHolder& direction, const ValueHolder& station0, const ValueHolder& tile0); 
+
+  private:
+    Matrix<DComplex> evaluate_itrf(
+      const Station::ConstPtr &station, double time, double freq, double freq0,
+      const vector3r_t &direction, const vector3r_t &station0, 
+      const vector3r_t &tile0) const;
+
+    Matrix<DComplex> evaluate(const Station::ConstPtr &station, double time,
+      double freq,  double freq0) const;
+
+    Cube<DComplex> evaluate(const Station::ConstPtr &station, double time,
+      const Vector<Double> &freq, const Vector<Double> &freq0) const;
+
+    void invert(matrix22c_t &in) const;
+    void invert(diag22c_t &in) const;
+
+    //# Data members.
+    bool                  itsInverse;
+    bool                  itsUseElementResponse;
+    bool                  itsUseArrayFactor;
+    bool                  itsUseChanFreq;
+    Vector<Station::Ptr>  itsStations;
+    Vector<Double>        itsChanFreq;
+    Vector<Double>        itsRefFreq;
+
+    vector3r_t            itsRefPosition;
+    ITRFDirection::Ptr    itsRefDelay;
+    ITRFDirection::Ptr    itsRefTile;
+
+    ITRFDirection::Ptr    itsDirection;
+  };
+
+  PyStationResponse::PyStationResponse(const string &name, bool inverse,
+    bool useElementResponse, bool useArrayFactor, bool useChanFreq)
+    : itsInverse(inverse),
+      itsUseElementResponse(useElementResponse),
+      itsUseArrayFactor(useArrayFactor),
+      itsUseChanFreq(useChanFreq)
+  {
+    MeasurementSet ms(name);
+
+    // Read spectral window id.
+    const unsigned int idDataDescription = 0;
+    ROMSDataDescColumns desc(ms.dataDescription());
+    ASSERT(desc.nrow() > idDataDescription);
+    ASSERT(!desc.flagRow()(idDataDescription));
+
+    // Read the spectral information.
+    const unsigned int idWindow = desc.spectralWindowId()(idDataDescription);
+    ROMSSpWindowColumns window(ms.spectralWindow());
+    ASSERT(window.nrow() > idWindow);
+    ASSERT(!window.flagRow()(idWindow));
+
+    itsChanFreq = window.chanFreq()(idWindow);
+    itsRefFreq = Vector<Double>(itsChanFreq.size(),
+      window.refFrequency()(idWindow));
+
+    // Read the station information.
+    ROMSAntennaColumns antenna(ms.antenna());
+    itsStations.resize(antenna.nrow());
+    for(unsigned int i = 0; i < antenna.nrow(); ++i)
+    {
+      itsStations(i) = readStation(ms, i);
+    }
+
+    // Read observatory position. If unknown, default to the position of the
+    // first station.
+    unsigned int idObservation = 0;
+    MPosition refPosition = readObservatoryPosition(ms, idObservation,
+      toMPositionITRF(itsStations(0)->position()));
+    itsRefPosition = fromMPosition(MPosition::Convert(refPosition,
+      MPosition::ITRF)());
+
+    // Read the reference directions.
+    unsigned int idField = 0;
+    itsRefDelay.reset(new ITRFDirection(itsRefPosition,
+      fromMDirection(MDirection::Convert(readDelayReference(ms, idField),
+      MDirection::J2000)())));
+
+    itsRefTile.reset(new ITRFDirection(itsRefPosition,
+      fromMDirection(MDirection::Convert(readTileReference(ms, idField),
+      MDirection::J2000)())));
+
+    itsDirection.reset(new ITRFDirection(itsRefPosition,
+      fromMDirection(MDirection::Convert(readPhaseReference(ms, idField),
+      MDirection::J2000)())));
+  }
+
+  string PyStationResponse::version(const string& type) const
+  {
+    return Version::getInfo<pystationresponseVersion>("stationresponse", type);
+  }
+
+  void PyStationResponse::setRefDelay(double ra, double dec)
+  {
+    vector2r_t direction = {{ra, dec}};
+    itsRefDelay.reset(new ITRFDirection(itsRefPosition, direction));
+  }
+
+  ValueHolder PyStationResponse::getRefDelay(real_t time)
+  {
+    vector3r_t refDelay=itsRefDelay->at(time);
+    Vector<Double> result(3);
+    result(0)=refDelay[0]; result(1)=refDelay[1]; result(2)=refDelay[2];
+
+    return ValueHolder(result);
+  }
+
+  void PyStationResponse::setRefTile(double ra, double dec)
+  {
+    vector2r_t direction = {{ra, dec}};
+    itsRefTile.reset(new ITRFDirection(itsRefPosition, direction));
+  }
+
+  ValueHolder PyStationResponse::getRefTile(real_t time)
+  {
+    vector3r_t refTile=itsRefTile->at(time);
+    Vector<Double> result(3);
+    result(0)=refTile[0]; result(1)=refTile[1]; result(2)=refTile[2];
+
+    return ValueHolder(result);
+  }
+
+  void PyStationResponse::setDirection(double ra, double dec)
+  {
+    vector2r_t direction = {{ra, dec}};
+    itsDirection.reset(new ITRFDirection(itsRefPosition, direction));
+  }
+
+  ValueHolder PyStationResponse::getDirection(real_t time)
+  {
+    vector3r_t direction=itsDirection->at(time);
+    Vector<Double> result(3);
+    result(0)=direction[0]; result(1)=direction[1]; result(2)=direction[2];
+
+    return ValueHolder(result);
+  }
+
+  ValueHolder PyStationResponse::evaluate0(double time)
+  {
+    Array<DComplex> result(IPosition(4, 2, 2, itsChanFreq.size(),
+      itsStations.size()));
+
+    for(unsigned int i = 0; i < itsStations.size(); ++i)
+    {
+      IPosition start(4, 0, 0, 0, i);
+      IPosition end(4, 1, 1, itsChanFreq.size() - 1, i);
+      Cube<DComplex> slice = result(start, end).nonDegenerate();
+      if(itsUseChanFreq)
+      {
+        slice = evaluate(itsStations(i), time, itsChanFreq, itsChanFreq);
+      }
+      else
+      {
+        slice = evaluate(itsStations(i), time, itsChanFreq, itsRefFreq);
+      }
+    }
+
+    return ValueHolder(result);
+  }
+
+  ValueHolder PyStationResponse::evaluate1(double time, int station)
+  {
+    ASSERTSTR(station >= 0 && static_cast<size_t>(station)
+      < itsStations.size(), "invalid station number: " << station);
+
+    if(itsUseChanFreq)
+    {
+      return ValueHolder(evaluate(itsStations(station), time, itsChanFreq,
+        itsChanFreq));
+    }
+
+    return ValueHolder(evaluate(itsStations(station), time, itsChanFreq,
+      itsRefFreq));
+  }
+
+  ValueHolder PyStationResponse::evaluate2(double time, int station,
+    int channel)
+  {
+    ASSERTSTR(station >= 0 && static_cast<size_t>(station)
+      < itsStations.size(), "invalid station number: " << station);
+    ASSERTSTR(channel >= 0 && static_cast<size_t>(channel)
+      < itsChanFreq.size(), "invalid channel number: " << channel);
+
+
+    double freq = itsChanFreq(channel);
+    if(itsUseChanFreq)
+    {
+      return ValueHolder(evaluate(itsStations(station), time, freq, freq));
+    }
+
+    double freq0 = itsRefFreq(channel);
+    return ValueHolder(evaluate(itsStations(station), time, freq, freq0));
+  }
+
+  ValueHolder PyStationResponse::evaluate3(double time, int station,
+    double freq)
+  {
+    ASSERTSTR(station >= 0 && static_cast<size_t>(station)
+      < itsStations.size(), "invalid station number: " << station);
+
+    if(itsUseChanFreq)
+    {
+      return ValueHolder(evaluate(itsStations(station), time, freq, freq));
+    }
+
+    double freq0 = itsRefFreq(0);
+    return ValueHolder(evaluate(itsStations(station), time, freq, freq0));
+  }
+
+  ValueHolder PyStationResponse::evaluate4(double time, int station, double freq, const ValueHolder& vh_direction, const ValueHolder& vh_station0, const ValueHolder& vh_tile0)
+  {
+    ASSERT (vh_direction.dataType() == TpArrayDouble);
+    ASSERT (vh_station0.dataType() == TpArrayDouble);
+    ASSERT (vh_tile0.dataType() == TpArrayDouble);
+    Array<Double> arr_dir(vh_direction.asArrayDouble());
+    Array<Double> st0_dir(vh_station0.asArrayDouble());
+    Array<Double> tile_dir(vh_tile0.asArrayDouble());
+    vector3r_t direction={{arr_dir.data()[0],arr_dir.data()[1],arr_dir.data()[2]}};
+    vector3r_t station0 ={{st0_dir.data()[0],st0_dir.data()[1],st0_dir.data()[2]}};
+    vector3r_t tile0    ={{tile_dir.data()[0],tile_dir.data()[1],tile_dir.data()[2]}};
+
+    if(itsUseChanFreq)
+    {
+      return ValueHolder(evaluate_itrf(itsStations(station), time, freq, freq, 
+                                       direction, station0, tile0));
+    }
+
+    double freq0 = itsRefFreq(0);
+    return ValueHolder(evaluate_itrf(itsStations(station), time, freq, freq0,
+                                     direction, station0, tile0));
+  }
+
+  Cube<DComplex> PyStationResponse::evaluate(const Station::ConstPtr &station,
+    double time, const Vector<Double> &freq, const Vector<Double> &freq0) const
+  {
+    Cube<DComplex> result(2, 2, freq.size(), 0.0);
+    if(itsUseArrayFactor)
+    {
+      vector3r_t direction = itsDirection->at(time);
+      vector3r_t station0 = itsRefDelay->at(time);
+      vector3r_t tile0 = itsRefTile->at(time);
+
+      if(itsUseElementResponse)
+      {
+        for(unsigned int i = 0; i < freq.size(); ++i)
+        {
+          matrix22c_t response = station->response(time, freq(i), direction,
+            freq0(i), station0, tile0);
+
+          if(itsInverse)
+          {
+            invert(response);
+          }
+
+          result(0, 0, i) = response[0][0];
+          result(1, 0, i) = response[0][1];
+          result(0, 1, i) = response[1][0];
+          result(1, 1, i) = response[1][1];
+        }
+      }
+      else
+      {
+        for(unsigned int i = 0; i < freq.size(); ++i)
+        {
+          diag22c_t af = station->arrayFactor(time, freq(i), direction,
+            freq0(i), station0, tile0);
+
+          if(itsInverse)
+          {
+            invert(af);
+          }
+
+          result(0, 0, i) = af[0];
+          result(1, 1, i) = af[1];
+        }
+      }
+    }
+    else if(itsUseElementResponse)
+    {
+      // For a station with multiple antenna fields, need to select for which
+      // field the element response will be evaluated. Here the first field of the
+      // station is always selected.
+      AntennaField::ConstPtr field = *station->beginFields();
+
+      vector3r_t direction = itsDirection->at(time);
+      for(unsigned int i = 0; i < freq.size(); ++i)
+      {
+        matrix22c_t response = field->elementResponse(time, freq(i),
+          direction);
+
+        if(itsInverse)
+        {
+          invert(response);
+        }
+
+        result(0, 0, i) = response[0][0];
+        result(1, 0, i) = response[0][1];
+        result(0, 1, i) = response[1][0];
+        result(1, 1, i) = response[1][1];
+      }
+    }
+    else
+    {
+      for(unsigned int i = 0; i < freq.size(); ++i)
+      {
+        result(0, 0, i) = 1.0;
+        result(1, 1, i) = 1.0;
+      }
+    }
+
+    return result;
+  }
+
+  Matrix<DComplex> PyStationResponse::evaluate_itrf(
+    const Station::ConstPtr &station, double time, double freq, double freq0,
+    const vector3r_t &direction, const vector3r_t &station0, 
+    const vector3r_t &tile0) const
+  {
+    Matrix<DComplex> result(2, 2, 0.0);
+    if(itsUseArrayFactor)
+    {
+      if(itsUseElementResponse)
+      {
+        matrix22c_t response = station->response(time, freq, direction, freq0,
+          station0, tile0);
+
+        if(itsInverse)
+        {
+          invert(response);
+        }
+
+        result(0, 0) = response[0][0];
+        result(1, 0) = response[0][1];
+        result(0, 1) = response[1][0];
+        result(1, 1) = response[1][1];
+      }
+      else
+      {
+        diag22c_t af = station->arrayFactor(time, freq, direction, freq0,
+          station0, tile0);
+
+        if(itsInverse)
+        {
+          invert(af);
+        }
+
+        result(0, 0) = af[0];
+        result(1, 1) = af[1];
+      }
+    }
+    else if(itsUseElementResponse)
+    {
+      // For a station with multiple antenna fields, need to select for which
+      // field the element response will be evaluated. Here the first field of
+      // the station is always selected.
+      AntennaField::ConstPtr field = *station->beginFields();
+
+      matrix22c_t response = field->elementResponse(time, freq,
+        direction);
+
+      if(itsInverse)
+      {
+        invert(response);
+      }
+
+      result(0, 0) = response[0][0];
+      result(1, 0) = response[0][1];
+      result(0, 1) = response[1][0];
+      result(1, 1) = response[1][1];
+    }
+    else
+    {
+      result(0, 0) = 1.0;
+      result(1, 1) = 1.0;
+    }
+
+    return result;
+  }
+
+  Matrix<DComplex> PyStationResponse::evaluate(const Station::ConstPtr &station,
+    double time, double freq,  double freq0) const
+  {
+    vector3r_t direction;
+    vector3r_t station0;
+    vector3r_t tile0;
+    if (itsUseArrayFactor) {
+      direction = itsDirection->at(time);
+      station0 = itsRefDelay->at(time);
+      tile0 = itsRefTile->at(time);
+    } else if (itsUseElementResponse) {
+      direction = itsDirection->at(time);
+    }
+    return evaluate_itrf(station, time, freq, freq0, direction, station0, tile0);
+  }
+
+  void PyStationResponse::invert(matrix22c_t &in) const
+  {
+    complex_t invDet = 1.0 / (in[0][0] * in[1][1] - in[0][1] * in[1][0]);
+
+    complex_t tmp = in[1][1];
+    in[1][1] = in[0][0];
+    in[0][0] = tmp;
+
+    in[0][0] *= invDet;
+    in[0][1] *= -invDet;
+    in[1][0] *= -invDet;
+    in[1][1] *= invDet;
+  }
+
+  void PyStationResponse::invert(diag22c_t &in) const
+  {
+    DComplex invDet = 1.0 / (in[0] * in[1]);
+    DComplex tmp = in[1];
+    in[1] = in[0];
+    in[0] = tmp;
+
+    in[0] *= invDet;
+    in[1] *= invDet;
+  }
+
+  // Now define the interface in Boost-Python.
+  void pystationresponse()
+  {
+    class_<PyStationResponse> ("StationResponse",
+        init<std::string, bool, bool, bool, bool>())
+      .def ("version", &PyStationResponse::version,
+        (boost::python::arg("type")="other"))
+      .def ("setRefDelay", &PyStationResponse::setRefDelay,
+        (boost::python::arg("ra"), boost::python::arg("dec")))
+      .def ("getRefDelay", &PyStationResponse::getRefDelay,
+        (boost::python::arg("time")))
+      .def ("setRefTile", &PyStationResponse::setRefTile,
+        (boost::python::arg("ra"), boost::python::arg("dec")))
+      .def ("getRefTile", &PyStationResponse::getRefTile,
+        (boost::python::arg("time")))
+      .def ("setDirection", &PyStationResponse::setDirection,
+        (boost::python::arg("ra"), boost::python::arg("dec")))
+      .def ("getDirection", &PyStationResponse::getDirection,
+        (boost::python::arg("time")))
+      .def ("evaluate0", &PyStationResponse::evaluate0,
+        (boost::python::arg("time")))
+      .def ("evaluate1", &PyStationResponse::evaluate1,
+      (boost::python::arg("time"), boost::python::arg("station")))
+      .def ("evaluate2", &PyStationResponse::evaluate2,
+      (boost::python::arg("time"), boost::python::arg("station"),
+         boost::python::arg("channel")))
+      .def ("evaluate3", &PyStationResponse::evaluate3,
+      (boost::python::arg("time"), boost::python::arg("station"),
+         boost::python::arg("freq")))
+      .def ("evaluate4", &PyStationResponse::evaluate4,
+      (boost::python::arg("time"), boost::python::arg("station"),
+         boost::python::arg("freq"), boost::python::arg("direction"),
+         boost::python::arg("station0"), boost::python::arg("tile0")))
+      ;
+  }
+
+  namespace
+  {
+    MPosition toMPositionITRF(const vector3r_t &position)
+    {
+      MVPosition mvITRF(position[0], position[1], position[2]);
+      return MPosition(mvITRF, MPosition::ITRF);
+    }
+
+    vector3r_t fromMPosition(const MPosition &position)
+    {
+      MVPosition mvPosition = position.getValue();
+      vector3r_t result = {{mvPosition(0), mvPosition(1), mvPosition(2)}};
+      return result;
+    }
+
+    vector3r_t fromMDirection(const MDirection &direction)
+    {
+      MVDirection mvDirection = direction.getValue();
+      vector3r_t result = {{mvDirection(0), mvDirection(1), mvDirection(2)}};
+      return result;
+    }
+
+    bool hasColumn(const Table &table, const string &column)
+    {
+      return table.tableDesc().isColumn(column);
+    }
+
+    bool hasSubTable(const Table &table, const string &name)
+    {
+      return table.keywordSet().isDefined(name);
+    }
+
+    Table getSubTable(const Table &table, const string &name)
+    {
+      return table.keywordSet().asTable(name);
+    }
+
+    MPosition readObservatoryPosition(const MeasurementSet &ms,
+      unsigned int idObservation, const MPosition &defaultPosition)
+    {
+      // Get the instrument position in ITRF coordinates, or use the centroid
+      // of the station positions if the instrument position is unknown.
+      ROMSObservationColumns observation(ms.observation());
+      ASSERT(observation.nrow() > idObservation);
+      ASSERT(!observation.flagRow()(idObservation));
+
+      // Read observatory name and try to look-up its position.
+      const string observatory = observation.telescopeName()(idObservation);
+
+      // Look-up observatory position, default to specified default position.
+      MPosition position(defaultPosition);
+      MeasTable::Observatory(position, observatory);
+      return position;
+    }
+
+    MDirection readPhaseReference(const MeasurementSet &ms,
+      unsigned int idField)
+    {
+      ROMSFieldColumns field(ms.field());
+      ASSERT(field.nrow() > idField);
+      ASSERT(!field.flagRow()(idField));
+
+      return field.phaseDirMeas(idField);
+    }
+
+    MDirection readDelayReference(const MeasurementSet &ms,
+      unsigned int idField)
+    {
+      ROMSFieldColumns field(ms.field());
+      ASSERT(field.nrow() > idField);
+      ASSERT(!field.flagRow()(idField));
+
+      return field.delayDirMeas(idField);
+    }
+
+    MDirection readTileReference(const MeasurementSet &ms,
+      unsigned int idField)
+    {
+      // The MeasurementSet class does not support LOFAR specific columns, so we
+      // use ROArrayMeasColumn to read the tile beam reference direction.
+      Table tab_field = getSubTable(ms, "FIELD");
+
+      static const String columnName = "LOFAR_TILE_BEAM_DIR";
+      if(hasColumn(tab_field, columnName))
+      {
+        ROArrayMeasColumn<MDirection> c_direction(tab_field, columnName);
+        if(c_direction.isDefined(idField))
+        {
+          return c_direction(idField)(IPosition(1, 0));
+        }
+      }
+
+      // By default, the tile beam reference direction is assumed to be equal
+      // to the station beam reference direction (for backward compatibility,
+      // and for non-HBA measurements).
+      return readDelayReference(ms, idField);
+    }
+  } //# unnamed namespace
+
+} //# namespace BBS
+} //# namespace LOFAR
+
+// Define the python module itself.
+BOOST_PYTHON_MODULE(_stationresponse)
+{
+  casacore::pyrap::register_convert_excp();
+  casacore::pyrap::register_convert_basicdata();
+  casacore::pyrap::register_convert_casa_valueholder();
+
+  LOFAR::BBS::pystationresponse();
+}
diff --git a/CEP/Calibration/pystationresponse/test/CMakeLists.txt b/CEP/Calibration/pystationresponse/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..64287aff04e0cb41e5cd9c12f7fc3290a4302eb5
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/CMakeLists.txt
@@ -0,0 +1,15 @@
+# $Id$
+
+include(LofarCTest)
+
+include(LofarFindPackage)
+
+lofar_find_package(Casacore REQUIRED COMPONENTS python)
+if(CASA_PYTHON3_LIBRARY)
+  #This test is disabled due to boost-python linking problems on CEP3
+  #lofar_add_test(tStationBeamNCP)
+else(CASA_PYTHON3_LIBRARY)
+  message(WARNING "Python-casacore was not found, disabling tStationBeamNCP")
+endif(CASA_PYTHON3_LIBRARY)
+
+lofar_add_test(tpystationresponse)
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..b095e0ebb3d0255ac0e25b70ca2ea04b978befcf
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..b4c0b8a5f7ab40e29381631342f0047f8c70d5f6
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..5e5472154b432f2e0131195718fbac5ec52d6728
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..20ab416e6b2089d0bb7ed946963b87aa3a535c04
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..bc76518f7b4e25a64bb5a1e2570ddf4df13bfffa
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..c985340dbb53f0a213d568ce9ccdc2911fc9b2f0
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..be5d92ea9a98d4c6f864d6d6bee56e4ded5392c3
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..82dce6ce873ae5a5f52f504f12e8d5a19af459eb
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0i
new file mode 100644
index 0000000000000000000000000000000000000000..b3032435e45263162440d053edccfc677ce9d36d
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..9f801dacd75bea58e64925ea82f4964e11374fc1
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..51bf40ea2f1d0e463019ed6e3f3a111f05309ce4
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..bb5818d375d65a5c60cef9c576cf15552c07d47c
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0i
new file mode 100644
index 0000000000000000000000000000000000000000..b6248a9d8abb5ed5e19cce8a510c83d7745f0de1
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..053e78092f1c8750ef25123f700a4e0a6583606c
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..360359641eafdad08c4c32e80d43b7af36c1328a
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..acf64ff0cee834d15e3054ae8c3bab48645789f0
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..6075ab6a530b38d4863db8006c50250ed8f94665
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..a1e994dbee2e354294c462ae3a48df687490ea08
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..9c5498713f1bb7d8f9450c22fe7af84cba01619f
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..9ecc6597675fd98c9ba1acc22a7181bcceef23ce
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..7a5778d435bd5cc2d50ec36ec0f4cb366d04d889
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..9de9acfef02ea9bdde35ac04f1b104878a109a10
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0i
new file mode 100644
index 0000000000000000000000000000000000000000..5c0f9a2a88a5e1772a7c868db8687e4458488920
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..4e5c1b43733f5aac86d4014f55083d1aedcff429
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..9ba0c7141eed0faa82b9afc1ea616ffea109bdf9
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..a890c6a767ea4d04d226092c34457cccd9f04000
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..f4914d9f8865ca727492994e62f19d07935280e2
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..c05c4861c9cc9b1b64985e0cc90e5f7d99ea2adf
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..1cfcc6106ee6149221b8fcef3d1c3540110ee95b
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..1c8629632ec05736163cbbd0c96acae378961301
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..e51b8f283d20f6d0e00166c2689297f87865e40d
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..d7262f7342b1b5a50c58f9fe060a4fccd703f6ec
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..27823d906cf5fd010896ba8ad05a589705305a6e
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..51f34d5a8a890e1f3d922d1247ce0328fb44d5e8
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..a6ee43a66924940443a9f8b1d61186599c928e70
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0i
new file mode 100644
index 0000000000000000000000000000000000000000..c09f05b309fbcc6ff12c9eb91aff39455347c41e
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..47dddc91a4a9c1eadf54ec7882fa8e13beb6ea5c
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..15d239d1be0e401e157d39b9a204533cbd948ad9
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..d487c40cb0089a9062007bdf010a13075950f19d
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0i
new file mode 100644
index 0000000000000000000000000000000000000000..247a4aff500069af7c07dbcd0073a7299984dba3
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..ecc3f973aab244303020d8132becf7db22bad38e
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..53616aa517a311fdc9c7880121036b145abe1591
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..db03d9ca6c2db7f53db2f0e93656a9b2041f7d66
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..9c961a98add6c1121e90197e8addec5732d3127e
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..df3191ed563dd7ef5a9a432c36cf410fdee946cb
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..f4af5e96200de834778618267c16a76e7aea6d03
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0i
new file mode 100644
index 0000000000000000000000000000000000000000..3d1e0e4020734dcf0c2bbe992804fb117e2cd285
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..5bbad139d9b1a15ea7c702c6e84484afb99990ad
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..943b7b45ecfdbac71d0acddb195a11faf4cf0098
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..f0f9754587b65cf2ca1a7aa5829e4e5f643b492b
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..a1a976ed49439abda34d2c64cfd0360cfcdef651
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.info
@@ -0,0 +1,3 @@
+Type = 
+SubType = 
+
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..7c926cb146a2f4fbcc57d1b4829c182797332869
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.dat b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.dat
new file mode 100644
index 0000000000000000000000000000000000000000..8bf109977b5c4824f5bd56dfa2e73f5d9a4c49ef
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.dat differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f0
new file mode 100644
index 0000000000000000000000000000000000000000..82aa58d25804a10c8d21471f7b2b8db954446b42
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1
new file mode 100644
index 0000000000000000000000000000000000000000..980491dbbbb03e5d108d452e1525c765cd68fb9d
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1i b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1i
new file mode 100644
index 0000000000000000000000000000000000000000..2308328019d48cc87144699973b89461338d1a0b
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1i differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2
new file mode 100644
index 0000000000000000000000000000000000000000..c2f9d67a5e6c106521419ada064547cb309d3888
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2_TSM0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2_TSM0
new file mode 100644
index 0000000000000000000000000000000000000000..548f6fc26a7832950aa1097322beb2c683756356
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2_TSM0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3
new file mode 100644
index 0000000000000000000000000000000000000000..b047086288f3e36a1c29991eec5b5ac539642030
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3_TSM0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3_TSM0
new file mode 100644
index 0000000000000000000000000000000000000000..10c971e1e574064accc23cc538c64c308d564882
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3_TSM0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4
new file mode 100644
index 0000000000000000000000000000000000000000..cf4efd44704f82e24355969a544d1eb3abc40bdc
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4_TSM0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4_TSM0
new file mode 100644
index 0000000000000000000000000000000000000000..7d46f8a944e0571e183a29d5695fbbebf3e4f24c
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4_TSM0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5
new file mode 100644
index 0000000000000000000000000000000000000000..ae90f9c3dda47e82404bbd5e63d99d0523aa9879
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5_TSM0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5_TSM0
new file mode 100644
index 0000000000000000000000000000000000000000..7df6a9895eb03ecc7df8419fc8148a1abb96ff21
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5_TSM0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6
new file mode 100644
index 0000000000000000000000000000000000000000..980b553c69975adc1f879e34dbeec0d9b10ace4e
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6_TSM0 b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6_TSM0
new file mode 100644
index 0000000000000000000000000000000000000000..3e414bb6fc1c2b199c0781a972f742ee10d496f7
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6_TSM0 differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.info b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.info
new file mode 100644
index 0000000000000000000000000000000000000000..105af65c13868a224843eb0cb3596828614e8fd0
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.info
@@ -0,0 +1,5 @@
+Type = Measurement Set
+SubType = 
+
+This is a MeasurementSet Table holding measurements from a Telescope
+This is a LOFAR MeasurementSet Table
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.lock b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.lock
new file mode 100644
index 0000000000000000000000000000000000000000..1cafbeea7d739556b7e3bd23d2865635f5154258
Binary files /dev/null and b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.lock differ
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.py b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.py
new file mode 100644
index 0000000000000000000000000000000000000000..bed4602c3395bfc69c8d824a9e02135c7b9c13b2
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.py
@@ -0,0 +1,25 @@
+"Test the Station Beam at the NCP. Rationale: when pointing at the NCP all stations should have (almost) the same beam"
+
+
+import sys
+
+import lofar.stationresponse as st
+import pyrap.tables as tab
+import numpy as np
+
+MSNAME='tStationBeamNCP.in.MS'
+
+myt=tab.table(MSNAME,ack=False)
+mys=st.stationresponse( msname=MSNAME, inverse=False, useElementResponse=True, useArrayFactor=False, useChanFreq=False)
+times=myt.getcol('TIME')
+mys.setDirection(0.01,0.5*np.pi)
+
+a=[mys.evaluateStation(time=times[0],station=st) for st in range(20)]
+
+for a1 in a:
+    for a2 in a:
+        if np.linalg.norm(a1-a2)>1.e-3:
+            print("a1=",a1,"\na2=",a2,"\nnorm=",np.linalg.norm(a1-a2))
+            sys.exit(1)
+
+sys.exit(0)
diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.sh b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.sh
new file mode 100755
index 0000000000000000000000000000000000000000..28e06978533ca6b511e00e32c1906da96c4db66f
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+./runctest.sh tStationBeamNCP
diff --git a/CEP/Calibration/pystationresponse/test/tpystationresponse.py b/CEP/Calibration/pystationresponse/test/tpystationresponse.py
new file mode 100644
index 0000000000000000000000000000000000000000..f69e21ff7d12d104e4cdefa8b988983b3d6ddb5a
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tpystationresponse.py
@@ -0,0 +1,5 @@
+
+
+import lofar.stationresponse
+
+
diff --git a/CEP/Calibration/pystationresponse/test/tpystationresponse.sh b/CEP/Calibration/pystationresponse/test/tpystationresponse.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f967776d2c39cf2a894a13444bb6bd9a53d5fc6c
--- /dev/null
+++ b/CEP/Calibration/pystationresponse/test/tpystationresponse.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+./runctest.sh tpystationresponse
diff --git a/CMake/LofarPackageList.cmake b/CMake/LofarPackageList.cmake
index 2320c39a326fe8770ae89ac84e902d4038841ed2..a7b835109903f83ffd08c7369215c379f1d785d8 100644
--- a/CMake/LofarPackageList.cmake
+++ b/CMake/LofarPackageList.cmake
@@ -27,6 +27,10 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED)
   set(pyparmdb_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/pyparmdb)
   set(BBSKernel_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Calibration/BBSKernel)
   set(BBSControl_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Calibration/BBSControl)
+  set(ExpIon_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Calibration/ExpIon)
+  set(pystationresponse_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Calibration/pystationresponse)
+  set(ElementResponse_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Calibration/ElementResponse)
+  set(StationResponse_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Calibration/StationResponse)
   set(DPPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/DPPP)
   set(TestDynDPPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/TestDynDPPP)
   set(PythonDPPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/PythonDPPP)