From 6b01695aa09fd4b211eeedc9c03ea2108a287eb7 Mon Sep 17 00:00:00 2001
From: Jan David Mol <mol@astron.nl>
Date: Thu, 11 Jun 2020 13:20:58 +0000
Subject: [PATCH] Revert "Removed station response packages, which are not used
 in LOFAR and externally available as
 https://github.com/lofar-astron/LOFARBeam". Was still needed by BBSKernel,
 oops.

This reverts commit 34770298013115c02c2140cd266a08a55ef6f0cb.
---
 CEP/Calibration/CMakeLists.txt                |    4 +
 .../ElementResponse/CMakeLists.txt            |   10 +
 .../include/ElementResponse/CMakeLists.txt    |   11 +
 .../include/ElementResponse/ElementResponse.h |  101 +
 .../ElementResponse/src/CMakeLists.txt        |    7 +
 .../ElementResponse/src/DefaultCoeffHBA.cc    |   74 +
 .../ElementResponse/src/DefaultCoeffLBA.cc    |   74 +
 .../ElementResponse/src/ElementResponse.cc    |  157 +
 .../ElementResponse/src/convert_coeff.py      |  176 ++
 .../src/element_beam_HBA.coeff                |  101 +
 .../src/element_beam_LBA.coeff                |  101 +
 CEP/Calibration/ExpIon/CMakeLists.txt         |    9 +
 CEP/Calibration/ExpIon/src/CMakeLists.txt     |   43 +
 CEP/Calibration/ExpIon/src/MMionosphere.py    |  674 +++++
 CEP/Calibration/ExpIon/src/PosTools.py        |  272 ++
 CEP/Calibration/ExpIon/src/__init__.py        |   26 +
 CEP/Calibration/ExpIon/src/acalc.py           |  234 ++
 CEP/Calibration/ExpIon/src/baselinefitting.cc |  228 ++
 CEP/Calibration/ExpIon/src/baselinefitting.py |   35 +
 CEP/Calibration/ExpIon/src/calibrate-ion      |  231 ++
 CEP/Calibration/ExpIon/src/client.py          |   41 +
 CEP/Calibration/ExpIon/src/error.py           |    6 +
 CEP/Calibration/ExpIon/src/fitClockTEC.py     |  938 ++++++
 CEP/Calibration/ExpIon/src/format.py          |  486 +++
 CEP/Calibration/ExpIon/src/io.py              |  308 ++
 CEP/Calibration/ExpIon/src/ionosphere.py      | 1380 +++++++++
 CEP/Calibration/ExpIon/src/mpfit.py           | 2593 +++++++++++++++++
 CEP/Calibration/ExpIon/src/parmdbmain.py      |   52 +
 CEP/Calibration/ExpIon/src/parmdbwriter.py    |   44 +
 CEP/Calibration/ExpIon/src/read_sagecal.py    |  223 ++
 CEP/Calibration/ExpIon/src/readms-part.py     |   34 +
 CEP/Calibration/ExpIon/src/readms-part.sh     |    8 +
 CEP/Calibration/ExpIon/src/readms.py          |  105 +
 CEP/Calibration/ExpIon/src/repairGlobaldb.py  |  361 +++
 CEP/Calibration/ExpIon/src/sphere.py          |  467 +++
 .../StationResponse/CMakeLists.txt            |   14 +
 .../include/StationResponse/AntennaField.h    |  261 ++
 .../include/StationResponse/AntennaFieldHBA.h |   73 +
 .../include/StationResponse/AntennaFieldLBA.h |   64 +
 .../include/StationResponse/AntennaModelHBA.h |   69 +
 .../include/StationResponse/AntennaModelLBA.h |   57 +
 .../include/StationResponse/CMakeLists.txt    |   23 +
 .../include/StationResponse/Constants.h       |   60 +
 .../StationResponse/DualDipoleAntenna.h       |   55 +
 .../include/StationResponse/ITRFDirection.h   |   65 +
 .../StationResponse/LofarMetaDataUtil.h       |   65 +
 .../include/StationResponse/MathUtil.h        |  172 ++
 .../include/StationResponse/Station.h         |  361 +++
 .../include/StationResponse/TileAntenna.h     |   68 +
 .../include/StationResponse/Types.h           |  234 ++
 .../StationResponse/src/AntennaField.cc       |  233 ++
 .../StationResponse/src/AntennaFieldHBA.cc    |   77 +
 .../StationResponse/src/AntennaFieldLBA.cc    |   54 +
 .../StationResponse/src/AntennaModelHBA.cc    |   64 +
 .../StationResponse/src/AntennaModelLBA.cc    |   36 +
 .../StationResponse/src/CMakeLists.txt        |   20 +
 .../StationResponse/src/DualDipoleAntenna.cc  |   51 +
 .../StationResponse/src/ITRFDirection.cc      |   77 +
 .../StationResponse/src/LofarMetaDataUtil.cc  |  335 +++
 .../StationResponse/src/MathUtil.cc           |   32 +
 .../StationResponse/src/Station.cc            |  176 ++
 .../StationResponse/src/TileAntenna.cc        |  100 +
 CEP/Calibration/StationResponse/src/Types.cc  |   32 +
 .../StationResponse/src/makeresponseimage.cc  |  636 ++++
 .../pystationresponse/CMakeLists.txt          |   11 +
 .../pystationresponse/src/CMakeLists.txt      |   28 +
 .../pystationresponse/src/__init__.py         |  210 ++
 .../src/pystationresponse.cc                  |  769 +++++
 .../pystationresponse/test/CMakeLists.txt     |   15 +
 .../tStationBeamNCP.in.MS/ANTENNA/table.dat   |  Bin 0 -> 3626 bytes
 .../tStationBeamNCP.in.MS/ANTENNA/table.f0    |  Bin 0 -> 17424 bytes
 .../tStationBeamNCP.in.MS/ANTENNA/table.info  |    3 +
 .../tStationBeamNCP.in.MS/ANTENNA/table.lock  |  Bin 0 -> 325 bytes
 .../DATA_DESCRIPTION/table.dat                |  Bin 0 -> 997 bytes
 .../DATA_DESCRIPTION/table.f0                 |  Bin 0 -> 1032 bytes
 .../DATA_DESCRIPTION/table.info               |    3 +
 .../DATA_DESCRIPTION/table.lock               |  Bin 0 -> 325 bytes
 .../test/tStationBeamNCP.in.MS/FEED/table.dat |  Bin 0 -> 4311 bytes
 .../test/tStationBeamNCP.in.MS/FEED/table.f0  |  Bin 0 -> 12800 bytes
 .../test/tStationBeamNCP.in.MS/FEED/table.f0i |  Bin 0 -> 4576 bytes
 .../tStationBeamNCP.in.MS/FEED/table.info     |    3 +
 .../tStationBeamNCP.in.MS/FEED/table.lock     |  Bin 0 -> 325 bytes
 .../tStationBeamNCP.in.MS/FIELD/table.dat     |  Bin 0 -> 4006 bytes
 .../test/tStationBeamNCP.in.MS/FIELD/table.f0 |  Bin 0 -> 5128 bytes
 .../tStationBeamNCP.in.MS/FIELD/table.f0i     |  Bin 0 -> 136 bytes
 .../tStationBeamNCP.in.MS/FIELD/table.info    |    3 +
 .../tStationBeamNCP.in.MS/FIELD/table.lock    |  Bin 0 -> 325 bytes
 .../tStationBeamNCP.in.MS/FLAG_CMD/table.dat  |  Bin 0 -> 2423 bytes
 .../tStationBeamNCP.in.MS/FLAG_CMD/table.f0   |  Bin 0 -> 2436 bytes
 .../tStationBeamNCP.in.MS/FLAG_CMD/table.info |    3 +
 .../tStationBeamNCP.in.MS/FLAG_CMD/table.lock |  Bin 0 -> 325 bytes
 .../tStationBeamNCP.in.MS/HISTORY/table.dat   |  Bin 0 -> 2592 bytes
 .../tStationBeamNCP.in.MS/HISTORY/table.f0    |  Bin 0 -> 614400 bytes
 .../tStationBeamNCP.in.MS/HISTORY/table.info  |    3 +
 .../tStationBeamNCP.in.MS/HISTORY/table.lock  |  Bin 0 -> 325 bytes
 .../LOFAR_ANTENNA_FIELD/table.dat             |  Bin 0 -> 3864 bytes
 .../LOFAR_ANTENNA_FIELD/table.f0              |  Bin 0 -> 15104 bytes
 .../LOFAR_ANTENNA_FIELD/table.f0i             |  Bin 0 -> 75712 bytes
 .../LOFAR_ANTENNA_FIELD/table.info            |    3 +
 .../LOFAR_ANTENNA_FIELD/table.lock            |  Bin 0 -> 325 bytes
 .../LOFAR_ELEMENT_FAILURE/table.dat           |  Bin 0 -> 1242 bytes
 .../LOFAR_ELEMENT_FAILURE/table.f0            |  Bin 0 -> 1024 bytes
 .../LOFAR_ELEMENT_FAILURE/table.info          |    3 +
 .../LOFAR_ELEMENT_FAILURE/table.lock          |  Bin 0 -> 325 bytes
 .../LOFAR_STATION/table.dat                   |  Bin 0 -> 923 bytes
 .../LOFAR_STATION/table.f0                    |  Bin 0 -> 2060 bytes
 .../LOFAR_STATION/table.info                  |    3 +
 .../LOFAR_STATION/table.lock                  |  Bin 0 -> 325 bytes
 .../OBSERVATION/table.dat                     |  Bin 0 -> 9335 bytes
 .../OBSERVATION/table.f0                      |  Bin 0 -> 30860 bytes
 .../OBSERVATION/table.info                    |    3 +
 .../OBSERVATION/table.lock                    |  Bin 0 -> 325 bytes
 .../tStationBeamNCP.in.MS/POINTING/table.dat  |  Bin 0 -> 3428 bytes
 .../tStationBeamNCP.in.MS/POINTING/table.f0   |  Bin 0 -> 2436 bytes
 .../tStationBeamNCP.in.MS/POINTING/table.f0i  |  Bin 0 -> 16 bytes
 .../tStationBeamNCP.in.MS/POINTING/table.info |    3 +
 .../tStationBeamNCP.in.MS/POINTING/table.lock |  Bin 0 -> 325 bytes
 .../POLARIZATION/table.dat                    |  Bin 0 -> 1306 bytes
 .../POLARIZATION/table.f0                     |  Bin 0 -> 1800 bytes
 .../POLARIZATION/table.f0i                    |  Bin 0 -> 84 bytes
 .../POLARIZATION/table.info                   |    3 +
 .../POLARIZATION/table.lock                   |  Bin 0 -> 325 bytes
 .../tStationBeamNCP.in.MS/PROCESSOR/table.dat |  Bin 0 -> 1344 bytes
 .../tStationBeamNCP.in.MS/PROCESSOR/table.f0  |  Bin 0 -> 1540 bytes
 .../PROCESSOR/table.info                      |    3 +
 .../PROCESSOR/table.lock                      |  Bin 0 -> 325 bytes
 .../SPECTRAL_WINDOW/table.dat                 |  Bin 0 -> 5224 bytes
 .../SPECTRAL_WINDOW/table.f0                  |  Bin 0 -> 6408 bytes
 .../SPECTRAL_WINDOW/table.f0i                 |  Bin 0 -> 6320 bytes
 .../SPECTRAL_WINDOW/table.info                |    3 +
 .../SPECTRAL_WINDOW/table.lock                |  Bin 0 -> 325 bytes
 .../tStationBeamNCP.in.MS/STATE/table.dat     |  Bin 0 -> 2016 bytes
 .../test/tStationBeamNCP.in.MS/STATE/table.f0 |  Bin 0 -> 1548 bytes
 .../tStationBeamNCP.in.MS/STATE/table.info    |    3 +
 .../tStationBeamNCP.in.MS/STATE/table.lock    |  Bin 0 -> 325 bytes
 .../test/tStationBeamNCP.in.MS/table.dat      |  Bin 0 -> 8122 bytes
 .../test/tStationBeamNCP.in.MS/table.f0       |  Bin 0 -> 66048 bytes
 .../test/tStationBeamNCP.in.MS/table.f1       |  Bin 0 -> 33362 bytes
 .../test/tStationBeamNCP.in.MS/table.f1i      |  Bin 0 -> 16 bytes
 .../test/tStationBeamNCP.in.MS/table.f2       |  Bin 0 -> 276 bytes
 .../test/tStationBeamNCP.in.MS/table.f2_TSM0  |  Bin 0 -> 24576 bytes
 .../test/tStationBeamNCP.in.MS/table.f3       |  Bin 0 -> 289 bytes
 .../test/tStationBeamNCP.in.MS/table.f3_TSM0  |  Bin 0 -> 1048576 bytes
 .../test/tStationBeamNCP.in.MS/table.f4       |  Bin 0 -> 289 bytes
 .../test/tStationBeamNCP.in.MS/table.f4_TSM0  |  Bin 0 -> 131072 bytes
 .../test/tStationBeamNCP.in.MS/table.f5       |  Bin 0 -> 292 bytes
 .../test/tStationBeamNCP.in.MS/table.f5_TSM0  |  Bin 0 -> 32768 bytes
 .../test/tStationBeamNCP.in.MS/table.f6       |  Bin 0 -> 299 bytes
 .../test/tStationBeamNCP.in.MS/table.f6_TSM0  |  Bin 0 -> 524288 bytes
 .../test/tStationBeamNCP.in.MS/table.info     |    5 +
 .../test/tStationBeamNCP.in.MS/table.lock     |  Bin 0 -> 349 bytes
 .../pystationresponse/test/tStationBeamNCP.py |   25 +
 .../pystationresponse/test/tStationBeamNCP.sh |    2 +
 .../test/tpystationresponse.py                |    5 +
 .../test/tpystationresponse.sh                |    2 +
 CMake/LofarPackageList.cmake                  |    4 +
 156 files changed, 14299 insertions(+)
 create mode 100644 CEP/Calibration/ElementResponse/CMakeLists.txt
 create mode 100644 CEP/Calibration/ElementResponse/include/ElementResponse/CMakeLists.txt
 create mode 100644 CEP/Calibration/ElementResponse/include/ElementResponse/ElementResponse.h
 create mode 100644 CEP/Calibration/ElementResponse/src/CMakeLists.txt
 create mode 100644 CEP/Calibration/ElementResponse/src/DefaultCoeffHBA.cc
 create mode 100644 CEP/Calibration/ElementResponse/src/DefaultCoeffLBA.cc
 create mode 100644 CEP/Calibration/ElementResponse/src/ElementResponse.cc
 create mode 100755 CEP/Calibration/ElementResponse/src/convert_coeff.py
 create mode 100644 CEP/Calibration/ElementResponse/src/element_beam_HBA.coeff
 create mode 100644 CEP/Calibration/ElementResponse/src/element_beam_LBA.coeff
 create mode 100644 CEP/Calibration/ExpIon/CMakeLists.txt
 create mode 100644 CEP/Calibration/ExpIon/src/CMakeLists.txt
 create mode 100755 CEP/Calibration/ExpIon/src/MMionosphere.py
 create mode 100644 CEP/Calibration/ExpIon/src/PosTools.py
 create mode 100755 CEP/Calibration/ExpIon/src/__init__.py
 create mode 100644 CEP/Calibration/ExpIon/src/acalc.py
 create mode 100644 CEP/Calibration/ExpIon/src/baselinefitting.cc
 create mode 100644 CEP/Calibration/ExpIon/src/baselinefitting.py
 create mode 100755 CEP/Calibration/ExpIon/src/calibrate-ion
 create mode 100644 CEP/Calibration/ExpIon/src/client.py
 create mode 100644 CEP/Calibration/ExpIon/src/error.py
 create mode 100644 CEP/Calibration/ExpIon/src/fitClockTEC.py
 create mode 100644 CEP/Calibration/ExpIon/src/format.py
 create mode 100644 CEP/Calibration/ExpIon/src/io.py
 create mode 100755 CEP/Calibration/ExpIon/src/ionosphere.py
 create mode 100644 CEP/Calibration/ExpIon/src/mpfit.py
 create mode 100644 CEP/Calibration/ExpIon/src/parmdbmain.py
 create mode 100755 CEP/Calibration/ExpIon/src/parmdbwriter.py
 create mode 100644 CEP/Calibration/ExpIon/src/read_sagecal.py
 create mode 100755 CEP/Calibration/ExpIon/src/readms-part.py
 create mode 100755 CEP/Calibration/ExpIon/src/readms-part.sh
 create mode 100755 CEP/Calibration/ExpIon/src/readms.py
 create mode 100644 CEP/Calibration/ExpIon/src/repairGlobaldb.py
 create mode 100644 CEP/Calibration/ExpIon/src/sphere.py
 create mode 100644 CEP/Calibration/StationResponse/CMakeLists.txt
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/AntennaField.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldHBA.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/AntennaFieldLBA.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/AntennaModelHBA.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/AntennaModelLBA.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/CMakeLists.txt
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/Constants.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/DualDipoleAntenna.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/ITRFDirection.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/LofarMetaDataUtil.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/MathUtil.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/Station.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/TileAntenna.h
 create mode 100644 CEP/Calibration/StationResponse/include/StationResponse/Types.h
 create mode 100644 CEP/Calibration/StationResponse/src/AntennaField.cc
 create mode 100644 CEP/Calibration/StationResponse/src/AntennaFieldHBA.cc
 create mode 100644 CEP/Calibration/StationResponse/src/AntennaFieldLBA.cc
 create mode 100644 CEP/Calibration/StationResponse/src/AntennaModelHBA.cc
 create mode 100644 CEP/Calibration/StationResponse/src/AntennaModelLBA.cc
 create mode 100644 CEP/Calibration/StationResponse/src/CMakeLists.txt
 create mode 100644 CEP/Calibration/StationResponse/src/DualDipoleAntenna.cc
 create mode 100644 CEP/Calibration/StationResponse/src/ITRFDirection.cc
 create mode 100644 CEP/Calibration/StationResponse/src/LofarMetaDataUtil.cc
 create mode 100644 CEP/Calibration/StationResponse/src/MathUtil.cc
 create mode 100644 CEP/Calibration/StationResponse/src/Station.cc
 create mode 100644 CEP/Calibration/StationResponse/src/TileAntenna.cc
 create mode 100644 CEP/Calibration/StationResponse/src/Types.cc
 create mode 100644 CEP/Calibration/StationResponse/src/makeresponseimage.cc
 create mode 100644 CEP/Calibration/pystationresponse/CMakeLists.txt
 create mode 100644 CEP/Calibration/pystationresponse/src/CMakeLists.txt
 create mode 100755 CEP/Calibration/pystationresponse/src/__init__.py
 create mode 100755 CEP/Calibration/pystationresponse/src/pystationresponse.cc
 create mode 100644 CEP/Calibration/pystationresponse/test/CMakeLists.txt
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/ANTENNA/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/DATA_DESCRIPTION/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.f0i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FEED/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.f0i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FIELD/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/FLAG_CMD/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/HISTORY/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.f0i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ANTENNA_FIELD/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_ELEMENT_FAILURE/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/LOFAR_STATION/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/OBSERVATION/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.f0i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POINTING/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.f0i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/POLARIZATION/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/PROCESSOR/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.f0i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/SPECTRAL_WINDOW/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/STATE/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.dat
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f1i
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f2_TSM0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f3_TSM0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f4_TSM0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f5_TSM0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.f6_TSM0
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.info
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.in.MS/table.lock
 create mode 100644 CEP/Calibration/pystationresponse/test/tStationBeamNCP.py
 create mode 100755 CEP/Calibration/pystationresponse/test/tStationBeamNCP.sh
 create mode 100644 CEP/Calibration/pystationresponse/test/tpystationresponse.py
 create mode 100755 CEP/Calibration/pystationresponse/test/tpystationresponse.sh

diff --git a/CEP/Calibration/CMakeLists.txt b/CEP/Calibration/CMakeLists.txt
index a3c04936e25..c957fbcc8da 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 00000000000..a5bc6b53164
--- /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 00000000000..a97059ef372
--- /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 00000000000..f95ba672544
--- /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 00000000000..fbbf6cf0cc5
--- /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 00000000000..c37da7e5e27
--- /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 00000000000..d196d2356d1
--- /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 00000000000..1324bfd4be5
--- /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 00000000000..03618ec0e31
--- /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 00000000000..f2d39e70295
--- /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 00000000000..7259a7d7cec
--- /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 00000000000..3c3ce32fcb3
--- /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 00000000000..7629de3ffc9
--- /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 00000000000..ced85cbfadf
--- /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 00000000000..e883734b163
--- /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 00000000000..21bc2dc8a36
--- /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 00000000000..f888fc74daa
--- /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 00000000000..792caea105a
--- /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 00000000000..4708eea1265
--- /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 00000000000..6811a1d0e0f
--- /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 00000000000..2d94ba48702
--- /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 00000000000..2e9ec7edc9e
--- /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 00000000000..5b753e87c4d
--- /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 00000000000..81b149105be
--- /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 00000000000..06b09f7eab8
--- /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 00000000000..429a0e597fc
--- /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 00000000000..2cf1ae18796
--- /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 00000000000..05eb57b3ef4
--- /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 00000000000..43a90cf0126
--- /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 00000000000..4a0fbb73bec
--- /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 00000000000..0e20fa37483
--- /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 00000000000..e4f916f81a2
--- /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 00000000000..4385626bd71
--- /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 00000000000..1ee4a8d8898
--- /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 00000000000..9a26e4563a2
--- /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 00000000000..16350a206a8
--- /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 00000000000..934e0b22472
--- /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 00000000000..6caafee4964
--- /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 00000000000..f4c08254291
--- /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 00000000000..9ef452c0d5b
--- /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 00000000000..7b6bfa543c6
--- /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 00000000000..b4dc8dff3c3
--- /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 00000000000..c77be6d22cd
--- /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 00000000000..4ac6439fc6e
--- /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 00000000000..280a6111a99
--- /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 00000000000..57327b0ed0e
--- /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 00000000000..cb16fb898c2
--- /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 00000000000..0019c33dcdf
--- /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 00000000000..3695aa3e816
--- /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 00000000000..8565e127528
--- /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 00000000000..d3ca5879353
--- /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 00000000000..ff099fb04a2
--- /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 00000000000..90838c135e6
--- /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 00000000000..52ca0accc54
--- /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 00000000000..87d02b94501
--- /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 00000000000..e31a7a54574
--- /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 00000000000..659d9f45ddb
--- /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 00000000000..f7f9c49e025
--- /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 00000000000..4d96af8dfe4
--- /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 00000000000..1ce4c5af2ee
--- /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 00000000000..7c4e21045a4
--- /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 00000000000..2813563ec96
--- /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 00000000000..5267ae75691
--- /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 00000000000..36a4276ffc9
--- /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 00000000000..e7c432c3b58
--- /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 00000000000..0d7aa5176a1
--- /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 00000000000..87961234ba1
--- /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 00000000000..828098ec170
--- /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 00000000000..64287aff04e
--- /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
GIT binary patch
literal 3626
zcmeHK&2G~`5Kd`A)0QBRiquvDRzj*;lvb#~0jVH4iA^mvKg20eF42-~8Vkn`_C`=7
zBn~_RCk{MH9)<@2zH#i-O&o%9Ksj`zv3F-?*E2gmJMQ=U{Y2vWRw9u|)%aUWU~mPv
z23!EHR4i`V0cZ6mD9iq&AiM@9BpQXsn5_B&Rnc(VCZ@E@CU9jy8Cgi&nHH@CTdymw
z%g^$T)$7=__d(O?!3@R%{?)Rk8EOr)tnyKK%xSeeA@O1zr)Nth9j2*jfmKyat*WKG
z%DRqeOUP#o$?Yb0n}#fMJC2YBF$E+eZUdL~isPBmbV!<rgo{AH!XrFlj^;DS<3~g1
zdhJDz+p^a=woU0_z7CB4%5MOe1nHux7<x%7W4r;JeDWks`jK84{}2xh>6-TGGZ0AW
zP<emcA;R#{#w^B`Cm_e$l9Xp%3awQj!D3ak@Cv8&sdV@J?2>w|s)0sy|1r(W=}Otq
zYkIi^`7Onk!nQekoju9EVcj<O1an18xWaA-xOeEHOAik`Jr5q<FdE$AZX}Th(a2a3
z0?FD*MaB4T=w1pbwk`Hf_n1*p^6G9*G1S5iM5g;S3_J+?FjEQgqZ7;NJ+A&p9m*7R
z<FH=Pm7-cxtF)rhKJ!c%&6+0f2q|1DN=?(F<eF{))t)GP!z3mpsSb>%BxyWTd7*{(
zffEY`3Ha#O3QR&E;`kxu9LHh+Fi9Y-9VyT2)$%KhZ)z4lWi7{LvTb_Ibv}SN{ahe)
z0cQ7Xyv8F@nJSi#OEr9PyEwQ`C@B4y<d)p!pR!E6?{Vq4Cbuw_jXlUtx8gs<=ZGQ6
z5+W12-3imnMo>ayO0Snk%Km<q<&_5y=T!S8qoxGqe-qa>UBPS{x2Jy(ba``!kva3C
zypFyj67@HZ%9>KG2PV|@0`%O_3(U0H1rC!zgEl>WQrIF2vu6Qzp+uN=0pA*ES~)}w
zt`B=DEWDQ!yn`(6puSyR#q-^4p^f|nm#qapC%=z{8fbpkf8JM<m>(nY%_*4FUr<LI
z?b7~zRe16HD}tTIZqq0;dW%Hq!&)6BhIKDWi&T>+IWAA5{E0c>3(@1RC-eiqV)z$J
zd<QNXMlm#X^f%-w7^$4)G~QuAyV9=^fKK@;aOn$9n(!-FNpuBxXYkJ8otU&3tSRsZ
DyY-&z

literal 0
HcmV?d00001

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
GIT binary patch
literal 17424
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
Y1%v`Z0il3UKqw#-5DEwd-dP3y0YjJH=>Px#

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
ycmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Q_jY7f1}smju$RPC5C>*-!zHA{GEdQ3qoH

literal 0
HcmV?d00001

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
GIT binary patch
literal 997
zcmc&z!AiqG5M3JsS`opUg1CN!H!o>pCD1k@2?V@^Z4x!O?v`{V;MwoWNB9T+h;b%e
zCE7?K7Y82Mee-shx0|V|sv^XACxn<GTks4P1Hg{P(Bmu@Ix~0!cE8!!*(%Xe_<YAD
ztPUE1pCz(LVT+Yc0mC)(>mX)(9Wr$3lOSQ76=TVtp9B`a8cE41gcj^grq)gDyAkXU
zC!F0=^^mV<Axk6z#YD=AG*B}px(MKYM6YJiWpfdm-8w>Hvs4R?Ja=mO&aD+X?mTvE
zpwB#+3zZdANxGEaHGg3$mx3z&UfWHWP^KOIO+Xbiv(?9O;9ZYH-<rmeGq>GnZRNaW
zWwp!_RWM%WB9-O8n5pafLmNU<h>g1ht{I5-Z-hb&9F&d+Yp1*K;Y)D`(+A^6762m%
fX1eA^6Bm#$M_eY4u)xFk_hak=UIBj!P((ffg$}-V

literal 0
HcmV?d00001

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
GIT binary patch
literal 1032
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NWxRGB7X!X%Gg1|3Cl~0s{^xzYa*l
z#2_-G<j@HLP>g~AFd6Kt0y-NMtHHs(o_Q&$6`)uJVFe%tB?k>4769Vl%-n*URR5xs
p)S}cB-^2ps*ayi=0x>HPJLTjjXTuBu^07;R6b!8GAg7IV002LhJ5m4u

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM6f*;9Rw!E%#CFQbPtJx4fE2L;06?$@O8@`>

literal 0
HcmV?d00001

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
GIT binary patch
literal 4311
zcmd5=-HzKt6i&#7&Hh!CtCk9BkPs?Rs%2?~D%wik#6d<IC&XSVxQQnAWMkI$SQDq|
zRzl(-5D&oxkFYPn`vBi@?BFC$6Rpa&BOQC@%$f1|KQq3(yu2)x-utUmDphUX_XRrd
z06qbf0lOWadto|rLjkjs3>sqOU_hk#_7H=;grF-Nsay<cmk$8DQ_R9bn!VDZ{dDVF
zno|6tF8y&3(%z3<IffXFrSr8;+cIpUZDRbOBS&8B$&kqb8wlYtC43%x&jpKRh8PGX
zLPxMDrNz1fE9Rlgm21T<9u^^LKvn@H&WC^<Bl}c}q=^aut^5Fw0ww(d^s<sj!~5Tl
zc^Hp_)6k1Y7~cXEf8`%EOj4`0bj>o%W*hz6fYml2YT}RhI!M&{*ou`Go;?MEn9i6_
z`T-$kk0C|{vOEI1SeC8$A|fZ-15m4U#Q?8tNS~_ZRKI1|-6n8^Paf;+y?3l@tzNs?
zv~(LwzBv{=nAs9`AB;rI?!pB|=NGI$j+qxSC|86bS0c0hK=D9)ishSGo_HfWk(!q*
zPm^fn6<+?vt^O65TWNKYilCx6#d7-k8Qs@y^c~kz!lB^A=GE_x4i68}S#9>IhccED
z-R!haw65`!Mxoxbf9&X(_}YfAQLIPO=gNDDP>10W>XV`83>jx<2>p<C^}61%+g)UX
zQ6xi`LTtU^^B(&u!(T1Etrom4gUA;zGQsyDc(1ElowjL_%%3;D2sonm1@rEU`^*>o
zoSzBS7x6?u)_5WzrX!+Qs?Frw!KT#KIr!_Ab0Lr4$gI@U%x_Of(vR(-fGk<e^6(64
ze<0PIwL!Pul$5=xk<wpj%HKgy;P{kcm$M!1cBRZs3-19|MkNstq~S>2DesoU;pn+y
zXoNANmJ;l0rmdT%)-xKw+%Xyy9BKBWL>Ldf5#v-)F}+@IRZ!*IiWIlH@??V0Ce**Z
zPJrzsyC>Y=G~C46gkgbng^vy1N}ij#-k559yGe%*k5cJd!RbPb)eN{t_Xq6+{5^#0
z2=$!%iIS%YGjR{9pOjKL)ku(x82OCjj1`Z?MuKJG2I8dshUfJ})o1w9+Ndg1UQA*t
z;ci3EJTOmN$ivf0VqxWxIS%?lk*;P5mr?nl7)uVHcOxbHICEdHFB#I-(PjsZ>kqp3
zt&U!|yV^<bnPE2C&!)co5wZtm+o@ATsND+AGVwxJP8ghI9Feluo}8~ioCPo6vW=Dw
z-k!8PH<BJQBuo^0!L)e%pPpW(TUCLlzwtBR=5ax(t3ZNBKrdH7Ix=i<-mi*CI&_h!
zoZ5A~X0eCM%-<MicTvJGITUco(IkJ*OE#cK@g)fpbdw?O7H$i(FVkG%k#de=@$5A>
zxqM)mo1QPl*%W;@SL5c;|CNzEI-j%i_@#W5pDd2(JVh2H8SYQ~C%}(_(jS0|Wwr1k
zUYE>4fmJ>BrE`uBEv9=q8Yz4WQ2n)3dKa(_pgK^)n@*U!1n4KH>Q$S7X2bR`T~<e-

literal 0
HcmV?d00001

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
GIT binary patch
literal 12800
zcmeI0J!n%=6vuBr)c760<5zutR5OTVaHt@X7Lh6xgIVGrB$z<NHil3nlN2eEp+g6U
z;8dJ~I0RfWg-nu3I!OkH4h|U{LZ{-Q|C9HBa+DS+)IOWu1E;_LyYKbgd(J(1@4j2F
z*XwtTG5g{1=~OPADx~An6RF$)8jRUz%plO;Juv16tdDwdr!jKTE4bIjQx;n$12Usx
zD1&+dqYl91@rk>+bf)Ods)IHZf(gTe@c5Id{AA{SA)P5?(i5q?cO6c3&mRMKz{e(^
zJ$~w)T(wdD!<x;2&4A5-&46DSkZ1o(bpGeFU*7r-d_B+pv$+1ZXTRL!9DEqQ>PdM)
zc<T#DYc>Nm12zLT12zLT12zNyI0N!MD^_yxYb(S1`0eofHOnQ+5}9!KN6aEQF54uq
z200>&AXt?QW4A<Xfeh_(kLSsNEWThf<O-SJ?Y6hc<R169MaK5J$4xRSi#1q{jL60g
ztU@{m+~YFI;hW=V7A^GjRi($2PAHvIdRFN-N>`QssPq@5mz546=iV5Gl)kR?J*AUM
zKUaEA>9<PPl>VgjSEauz9mJe_<GHN#O{E_wEw$oZ|3c~4O24CyB5tvo3ll-VVinTC
zbz)^Q$O$WX6UTZhk{vS3x?3U>th+^WoORbAM_6xFGR%5gAVaLTc{0Fyn;}=oJnODa
zCRukaGRC@Vl6f~g(<Wsu#ad*HaW}~*<F1hr##<pB##<(XjJHG@##<yiWR`I+kqO4V
zNRBh^206lbt7Mq*E|4L{J5L4}?+m#@N~{v9geM_M7&3Gjt_)3vBSVmGr7O9;+*1Di
zAY7hbhv0|dN8m@{vehQ_cLE-QpM;-+pN5}-%V*dSTt3U(-9P{SVhFz4+=r>r+2u#k
z?MkUTKi$8|b9*kuZe1zeSRUQ3^!3MQJGU9|IRim?iNZ=`_l1h%B0mf|WL93XJ!rD#
zXy66ltye!Q{Y~k9T<BdBQu-Qo52j{QLW;8PYGj1<Rv{hMUAaf=FVDE!WRh{W$Qa{p
z_GtVLs_Z=c0{kNU5_}lGS+>^tYqR?IyPx&0`~4p8M)!|L{Hp8!jqYcy`}tje>s|M+
z701?YAGnR3hW9cTh{M~C5cbJlU*AL9YV**o3tJEF@8fTIzz@cn&4A5-&4A5-&4A5-
J&A|VefuFZx>qr0q

literal 0
HcmV?d00001

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
GIT binary patch
literal 4576
zcmeH|u?@md3`7kZ(9tpgGte*s1F;#a&@uxJ`3fih7oDgnE>dFoboz;&&Q4X;^RPU=
z*M6zGrTwb%j#_oUcyH^w#W1|b^Y!*Vy&kGO$DcB*^!NF*Xa1hNcjZlPq~$vL!+B-Y
z9FnW#%FWGI%N%Wqj6QRu|DP&z;+RpRzj@NtdiGWJ6|>>H<jQf$9BEIfGAE81HTs(;
jU9D$dWnVEHzDuqgm&}p&lqz%Lm{Fs@dD7MTzwN6J^bt?H

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
zcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5UT+(4-hj!`I11I)hQ=GIU6bfQp5xRM*|0Q

literal 0
HcmV?d00001

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
GIT binary patch
literal 4006
zcmeHKOK;Oa5OzocNlSzTmlg?W#R0Xbswxl%P^si34h_l+I~C#-ZJkYGaJ-T2sLBC}
z1HXa;2mS?@{50Sj$1YBsC{QFQQb!u^?#w>sVb6Y^PN!2vx%EX+lx&-y+5(+x;8e&P
zEt^|Ts40AfG#?Dg!ZX9EXwK;gdYi!nU6|7CVn{pP1#iqG3kykmp+y_v)?2FU@<~bB
z<Do-)A9m#!ZZH<=^JT53zUh>!Is`{$%N3??Nr%ZE>k6Asm?<1zxNJx4_xG8u?y<5~
z+T|V_NqgeRp~Y=xIn1|)Vjnvc44*q)?sg5o&Yfj~9E3y^#cgoD+LE3{XJSC^hLl?2
z_Iv1)v7+5>z8Z7K9}iC)%l9ze0^b8CPDp(RK|z$OYpPLgRGR3MIjd7av&p{4;6f19
zL&JA1r+)-NI#iM!#A%mPa>M2=x=Ygj6yoKsWc<m9UbRgyQCb&0G|`aE$akY$VUT_K
z=-}W0ogjepSey6Ov1({x-goEnJ{oXOEYJH@if8HlFH&64wTh-|jZ&26(p;L)wJTD6
z^^H^Iyv*PDwqbIcyRqUvD@crml^|1UmNoRB86#m*r8WoL<tFO6<@uKBQI+pm!tU;|
zpu87RyGPvf81}VJUG7U4tJl}`s4ii0Kr2f3>e@;bD=1xIoAj(GrOiO$gj}VjzUb)9
z*BIw?dB!R>@1wuoR!o|si`z;DCk<UpK|`%m3T`*}P*A-6f#uDD4(WPx|3YZ1j!%(g
zu!mgAHBRcCR<rg7eb)MU%LA?e2fzcmG?*D63c6<3`;f(~I9)vC*kyUt7ktPY%@ZAU
zuUf{6ydlS~DVRk!@};nL0fS}Q)!-eu-v)|e(zW_PR6Xgk^DCa=!({j#2@|aMAc9z!
zOq6<n#}fGQ&ymza*tHT#QJw>)cZ@Q(#DhNqP+2jO<^UU~PuivMFf;+t!Rrvzom#V^
z>YaAArge&%S`V?*-f>X{90tTb6?_;=;Z6cEpY7t9Urc8RSqY*h@GN9RSBM!wi0b8U
zAZCR28-*{%iKIXm;QG1WC0Y>>!NGhSP|s-+IBZTE-O-Vd_KM$bad{@ZOyaV5xsJ={
zN?=?ORhzhM#k(9$Q9k2082`b_Cve&@>S)N$M3|mKm@V4Ue1{ILrr%Hybpy<dHuC}e
VQBkgEz-Vuv&4HCwi=kRWKL9?8@)7_5

literal 0
HcmV?d00001

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
GIT binary patch
literal 5128
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NWy6GB7X!X%Gg1|3JV9VKTI_K=^e)
zK1>WMHA)Sc5CFv}2mq78z9OKjIe<7g*w-^JCA9(;s|r9dE+EzbVgVoy&de>yN%b#E
zNi9lE@l7m%sRK%Z<wb!sD-b*7<R@ptB(VyDbc}-05Eu=CksSh{(x(a-1n8v?sPIq#
zVp2*UNuYX^(g#)oVU>VM0yT|-(GVC7fe{!2pf=4&1_mS9^FY4`jBx*tdVk<Uz{%Co
qHy)S~*#_Pzqb?YL5CHYT2f$h+D;tzMEI&CS35+Oa!HDuLre^^oQbiL0

literal 0
HcmV?d00001

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
GIT binary patch
literal 136
zcmZQzU|{F~Vi;foGC-ISh$sJCrRv+ma^QX`+tUl`Z|y;9kmW&q5RlMy3Hhz~6GI-G
Levr8!GtlJ#*~A>$

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM6mtP-CMcE!(yUH7`N`Q(NsuBY06|*^O#lD@

literal 0
HcmV?d00001

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
GIT binary patch
literal 2423
zcmcguPixyS6i=3<O_~*U8LVRkokrVHXb(FK${<aXVTjW#b_+eKw$g|>R<M<f9Cq7j
zhn@C$@@e)tw*8)Cw{Dcd7HA(lOZq+id4DC(=ks}?P>TzNLdE6Rfk5X8n5YQe>IGbe
zsbupfkXkZ06S0RuHWl53-r`$NCiI1;A|FHA<t2D)fh;W~=~|1b>DD`r#{(WURWKQa
zwE4-YQUL>{6E+*YUdL*kVfe<4CW6TU8wC7@4SCG2g$NlRj{_-urWG5(Sf*1yY&)8V
zK977y+dRa20|0i9*0O|Lka)>7#nF)rZ|+QKp4)b#OQt+)!-N|WdTKO+n!pV844_U&
z<!F6<u?gvIs_d%?vglUjs?~K{{mVuNlMh@ug8M@)qA3s9Kt=5HP<lhAhcaHo1T94J
z->jh&$kPuDRwUm!U^Dqh`==i#Jk*oXMJP2@zzgs$xMuZKEVWb=bedTF4MIyq;{)(^
zBZ~O!WD%nG07|JaSwMZ-k_H)3-*lK2YRXmyAx)@sT9+;I)+>rI3&d0e?2tubDq|>=
z0+1WQHGm3c<>>*Uy_BFE{Z_*{@8ZyUqJ1QIoH)_Y%>W)DHJ3bHb>Qi~<z7ME=MMZF
zNj*b3`GV~o9UdR;GYAYl4d4;Hyi`+V*RosC?bw$7SfQSn8a!NC6<_116=n4Wuvu30
zAgonZRU4^eb=y>}JK(=R84&(w5z=+C!h&~zS6filvoXDdSyGnzVvrjqbt=Zn8v-m{
zxJ@*KWkh|m(3fhZ>|XUKin}h(q6j9B(q6mM_`q;8UUaFqj|oP`89=6HcK-T>j%)5$
zx(gXWY@9}N3UlsZ&5TdryGJSy=O2PRetX~L!Ml4TkLC+Dk7xC8A6s}g!@lD`2R}N6
wZ{U*S*r^SMapbznX`sAM=+NTqktt*N6^4UL=$Fw}&~AW}ADESkAX`m;04h|(+5i9m

literal 0
HcmV?d00001

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
GIT binary patch
literal 2436
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NW;=GcYg$X(kW{2>t^Bhz|lBKn&uS
z0cn^RNPH9wr4RtcAP4}H!M-A(!$Gkc9PI0vmy%imi&X`vxCW3G0Lf?O7UZP*7p0^Y
xrKb2M7Qoa2rNHu{K$;bZopSP%vtg201wlGS!DtAKhQMeDjE2By2#m}S005EsDv1C9

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Q6{*5QEqtED6M{PC5C>*+_zn06@40M*si-

literal 0
HcmV?d00001

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
GIT binary patch
literal 2592
zcmd5;OK;Oa5Ozq@=0Q;pEou=+dqOSTka|EuNNyS`qq+{Ui^QqgI@@$1UK@J@^nk=~
z;K0wxPXoSj?ACcHP^nTz+RX0E>||!=<>7ERG>rOV!!RlyKko}{?iogD3%G<;Z}+)W
z8K>%jQcDkqBJoF}_`QLBY#XUSNBCjf#gVi;!m5ub6Bmhlt4p;^^|l$u{HhuD2Z18p
zuUs8V)nhE<*UfhOtZjBo+XdyJ8AW~Rb1g%~A|CTVXb~sith<`4E|0sewz--`Xn;sS
zChuU?tag}4(v%T_Qn-~rq%~E&$e~8_*g9)Ewr!pq;>sqD%nySA*0V%>9th=A5SPZE
z9oOf59?y?Z0}(p5nx<ztCop`Y7$SuL{g*QkPT}@3!1Xl3i_BiLTdr#!wIJU(4tp#R
zNy2+eW38n$BgovTIR|fBP4CP~bH<5fPbyRa3dK49D14pL7w1d`>WXj&&pcuH1LweP
zb>64hF|z)-bDoI!%czEBm+eZ`72lXttRTuXHM2d~x)e@LG^JIiV;xzP?JsuSa1i?f
zCEOKzEKCE#F5)m?UoWM9nWt68;aX5nUF=idtny&pL7*mls<t~8-10u)FE{K_F|a6>
za7kY+i_P6|fV8--iWSeYsem^<iSpGvh*)@$Klwk|T!mBtN>6c^F#x}VJUsw0XpQ^t
z23+YuaH^!HIkb)S1Z!%#l>Pv~AgJ0c)3r|CIM~xDnm=Djn%dbjto3wMW&`py2<41P
z0_h?HD8FWS@Dk+N9<1oAh}vGBa^;S=C>%*~MMR;0iB0*`YXT811-SGZ$1NOkBc^hj
z1nH&5C;7DsVol7%ao+fN$$50G(87!4v0hnZO!uN9_ok?e)M8l_>3Qnqv|soT!<U%x
y6RYgHHV~OO^%@k1%0WN$KVd_c=|c!m`Z0><3h*k<tH5i(Rp2_XG4CSr=EGmX5#;6o

literal 0
HcmV?d00001

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
GIT binary patch
literal 614400
zcmeFa37jNHbtl{cB&0!r!5k7GDJ2ckXy~cQqs~SJ^fh9{^mMDc1p*;j)!o_C#Z*@n
zRn;SngfKStE;3-&7B&W3fWa8!53`&$HW*_sKZCKsCuX&*&4&ex{b4K?J|8Up;QPNg
zvhs?mtgDYvv{aKXGb3NTc=00Q#fuj&KKaQ{e)8DZ*w|z7TWXc6>!rr}QfsbMeIy=?
zjXh>;?0?|$2zE0z_Cx%gC*Yqve{Adlz@NlVJ`=C>W#4dsYLxyU2A}+0P<<hOOG|Ud
ztLyHaRI3-^XNc<|Tz>?=rSew2;?6hL-G;k9SE~DKpWYYngK&&ZRcdReC6X)f;JNs%
z;P+$ry&b>b$L}ln{Sdz&MZ(w*3Bk4a+4#-kw}Rgt_`MOo_u=<h{PdsZfaZYa!2aaG
zuJ^r9(tYp#lpIa9=78qFARM5X%`H$?PaisO>@vWA@aVCzpG0>^@O=k3d^zB4B&6pA
z-+=TIz7z^G!B^lp{k>7Z{}p&hQ#J5=4(3wp0{-@+czSyNhe$)|34S~BBKS(cuNQd{
z{4$Xj!KTRTD!@;~kMbq>Ye@ey0{#@z|CE65LwbU*Mw&DD5q^S~Ko5OO@G~!H!~cf#
zgy$fhKZqZFOYm<Yy@c;Ydcs4`H!wN47w|IypTt$d&qNvT6VER|8DA&huOL0a*C738
zas7Gxh|f*nyck!4XOV_r;`6n5FX8Wj1_}R(NKa|*7wL)rL>HwY_;Dx;!3KWs6yFlu
z65kShllb;Zz|Rri68s~epS~sd??rloKO)i-&woLrC%7Tf6FiIZ(zgU(f;uJmYW&WB
zL>vB+NKen-E7BAEQzHFAz?&jH!OupW5*~st5#=JD(;q!2_)8)^!5<Up3I17;{u;n{
z;YaBSzD@9C{>0=0ZpT2<*woY#fn#_z5PWiGVS$i>j4?kvEd1vZ9#k9qL)A0+|1rp$
zgh@ULCOLN?ZxTKql_lXXiS(4_y&^ry|1XL31V0TjK<N$q-jB+a@T<kQ^!zsQ?UjIU
z6yFm343QVX|18oI{3((CD!}g)=?Q+7NKf!hkZsC~;DZ1NCOLn?BiitTB0W9-s7O!n
z&x-U10k=eYf~oH%JOm#U<svyJxu)j?KPb`@{ArP%;QK}TYXJWz{3!jy{ST~Sd^ia{
zQU6Q*dmi;k@U5t0f=RZDxJvl(s51$FRivjh9~S9J=H5**fp-M6dID?!{utzsz9sml
z#kT~X65n14cp2|04Z+ulya@hxk)GhsiS$<iejn0OdV*go(i40u<VikHp|S}k*?Kw(
zE#Ys8^z{7qMS6mNNu)mr_*Ei3!8d_V^ew?D$P>XNTTj7H!rv0<>G>B#dV=30(q9Ak
zzliiF=NIQDPmGPBXk!}Qc|N@Kjn%7OTlmi8#>P)v{xi6){>aogU0+fBnWJ?5$7^13
z&40o5#*2UPI=cSZzs)_1uD|iW@66Nn-pRWPbbVL-S8kx|Ert9Ey8iy#|L`PTulT9$
zTj+Xnb>d}o{lQ;f@|*YJ`nHE2`yslv?pyzDy8i2-_kV(}Ti>|oztQzmpF8ifbe+8Y
z)-TcZmp^^*f7119?|%1J>H3;az3m%x{mcvA@-4bP@P+Swhpv}C>ScGo3D@8HuKQ}b
z{_CMf{U^F!_tHndp02<4OJhGr*C)Q_s$Zll#14N4zRj;=kF1v(r7gGRHkwPVTBCHz
zoiNVerd+EUMZ-#2Ipa{dYD~|SPP-?{%~mtjy0bNb7v^;bj2cpwDix#Qo+-00@QIx>
zo%{i#URpa1WWiV2oKr9kRZ7j4v9?*N;x$l<lt8z-U9PkYWL(*-nx<u}o;519jZ%Z2
zuN$S7F~8O_Y}>F+%Q4Ktgyl@wwwG1Y*lxP(6UKo9hH<=#0&SGmdMS+2sW^rtv!?<X
z{S?VGsRVONC#dL&D#J`!;NG60-y-@M^=w@!Q4zR2?TsSX6KRKvFj=oxrfXYUwQ8aw
zID4oTPy|*BORa`m+Db(JUJ@cneox$Y_DmbFCmfShO=t>_`~!X+yNpRwt5HdnAXeqI
zQVTt7ak|!M&<iALC#W}SYi_gYHjB5?`vu(Ih6Jk~hKAeRuCzAF6}Ncp(OSKAl=YHF
z&y*TR8{5^Rlhe~H)6+Mj2c4L&R?1a(x>jv9YL#2f+wQgsmTB4dtjLU4IIa{M1+GOd
z`RUqfsnSYKL2i!K8e487HC@}px?i=0@!_=FOjR4x=)0?KrMciXmbO<{v4&jCrtxjM
z>$gpwoW49p<Yz2e<m;YitGwk>pMI*r_;#$chOw$>;oC!9^SooTytY|9j<gfew8yJ!
zNlTYa@O6ColH{u{;o$`Nw##ToQ_B+0xa;L+9b>yIE3mjxsx)12<~s2eiG^D!fw`%2
zo88Ocei1#inaw&#ED7=@t~S`xFV(BBnpg$JpjJ!JQw1HXw2-`1z6;qFvRN~aT%Rsp
ztr3Z9ttm*|LaEs-wi?@1f=h$1rwM`g@-p#q9c4R<PNnWvo4f&vub(6mptr2B(u(KL
zlI;YIfVafdihHNKw%sbNLVTxBu2AblV=Y%tfrzVPl5f_UttvFE;_9hpYs0PbqVQ(#
z!&<GaQ-i05elP}0vsG%enyp%W`D~p~QI9bmO?OAFaT=tomm5W2$FN$i9!2`6dFfYT
zh-&BR-q~uDj#oEo#aqc(G`?_r8d*`VK-8g%1o6C|VqO89aIiV>D$<2LgYdDgr8(Jf
zmm8&OvtEPJR$S(lcqMy3eUg~!7r>>KTW=PvjFSZcm--W--)favSGs7Lc=?9F%gs{L
zov*qJO}7Q!rnBQtdK`)H=jp$Xa_}@i?l|L4X57h*XUuWi8c*YiJ?<37)Ao1<x9Rar
zemo7p9M7c3)6RH0Gj3<c(@2j5fbpa-p0&p9{CL(J&)VZQupx1FJcBgOcoz6AR=IHm
zF5giD>!|+CR4VIGx~n7UmNy$Ny7FMM%j1)Y5)+~TPwT`H<R;#Gxjn^?2C_|>KCe^L
z_XeXzU?d<NK5iUHb@(Q}hqOC<q3#*4>hP7XNA#zP^!KVV1j-%xD!vryjyb8zaj@fe
z?UG!}(@_W5t&ctwPTHY|z9yPh4iLT(o(5Z9vB%2QQf1C<m1at<(ha2+$wVWyj+<3T
zSBm<UVzbm-U&R-9Fx>H9_&U;SPt2^GI6k#Fx%lFhg~{dT7mcHY@91W2%jMcl^KA1d
zI-K?GHEQib<Drf%7B^`!yjZcXIgNaliIlS)h-@}DkFP`dDYwotjpb;gR%;zi;qlS(
z`bwi&N-^aN<N?um2Sk{hHcrww;7fl6OMiS`(czJ9#CtoL1l=9_ve`xh+7FaV-fX;=
zc*Ps81!X>Grtt5$2ik2g(+0C`u+RoA(+4{~IPHUTJ~-co(R;S@^ks?;vHjeat?kPf
zJ*JliJ#riN<%>SL{oI!?dS~xBdhIss%NHX+`?)V)bo$<Nbk=RymoNJ9_H$pp==8nk
z==VL?@)ykVm(21P&BBn>{>EQ83&T?TjlX!7zkC*kB409J2`vms?Kl21VkGsy@l)V0
zriDSTjlf?}D^Sv`pMtEPf~=o{oR7;^&QC$kPeIPdXe;NZAn#+fmG|>H@24Q|ry%dA
zpkUI7$h*;SpB}4_@CHq-6k}htni5TIg{vqlLW60vipTK8>j0wZg=x+A!SXbve=O?&
z78Z_44d^odz^$&8E2M&@HY#VEvu9mTQ|DveLOTJJLbBSOg#OqbwQpu<M6RhxsPffn
z=~&sVtT$&!H%!vGx~;XNa1d^l*JNK0f?pVicnRti*t3xwub_xNOs(NdV7@7U`T0|m
zGj63-8jRdW{BZy&jZDz7Fg4h2nx-=@fV2cM639v*CxN^K3IaeE$v?Mj2}nGa#A8W3
zmc(O8JeI^`OFZZ|IYQ`?0+65E@^fgH{1v(r0m!eQu8Jp;4Cu4siNu56rBVZ{@#pEk
z=Myi`(}<*!EGWAC3HluY$PA$UiYGDybgSZtBoz9ycp?#^`{7TZP76RHL}w(PNYbHE
zizgB@x+U>MVuqT{pP*k7fW(6?NIa2WWxcN?J?M;hnw<PRC+R`gBwk5+a+02$#Eec#
zq>-5O5+Qmi{t7*m03;rCQ{sullb3k%9v(?yK_V<jgawJPz@`W<2nhF9^F@s^tlr5Q
zA!$ysQ`#z(X;zvGpzBHR;hmD~ZZK6n<+jkzV47ZQoaF=|13p)gsp9mZXEG8J*ekeJ
zv__h6)D-X}frY&khJa<{|8(|TX#==~zvd<q=$LbEskz;7i!`l#JbPDd@Ocw6c0G|j
z3K<N}yPZ94hWljausQA=w(!HO@-XIthtv3Big*~a&%-(VFyTC0zz_Wj{-KoUSnv;}
zwJ0%q75qcV(VO5O`T)HMFwz(3J@5~Gf$jtU&=)iXJ&ai@{-H0>dEg)V0=)<Rp)Y8X
zdzj|FhoOexANqnO#D~#U;2-(|lVDIwU!W_%KlBB<0Q^H=K>5Hw^abX^_=moL+JUms
z7ijDFhrU1?$3OH1+BW{7FVLp(4}FoLFVLXz4}AgU0{_q#S^5I%1^%HgvN??3=e^d?
zV>Uw7s-#(?YoA_%)e~cfIZlx6L5>HdJt*TrSr5ud$nvmQ-nW+b*7n}o9zNT{V&^3m
z$MhiY3rA7~L#%jfnKBQoY>8)%50YP`y=NH#EiPFm<vYH(WSdkV22lP4Sd={gN4|1o
zV#lPi&?}iHEnlVOtF&3*X)=sFB+i%ubB4VlU5e`~t+Km5*=Ur`25W;7nDeWc_?%(Z
zHYg$`vMa+7ox<v0#kGn~Drcp$xqL34$>wuT#>sU1#x9zvf`vcR0U2h&%2>=OsmKSW
zFmw@U&dhLjqir%X(Uq#y+uXp+8vSL8L@-;OW~NOQ7G|ttW<(U`as?LNOBRvhh(ARV
z{-{4tFz4F19O5|>AXgHz?q`b;hit31b$q7i*bq!I6vU>fHkM1Pi!e_%8GEmZ!-F(Z
zmMC+cGM1(Qldp@zC5$v&E6&)dl%2_tNCv7+77@k^B8}OcsmH%M4x^~EYn^GsE@a5a
z@HBt2>C#rcR6bQrP0z0!r<%>izFjz8ymp~{*Il)7W2TWZ7Hg|+qg6AGL9Sp*UaJw_
zXUBdCRk?+wkM5rbc<YhH%+R!Weq#RE<l;PY{tJkPmlEa<n72bo1EnF`56rY=k7=fk
zSL@rYuyqjfwN+}KCO!m8)BQ`gO4jvtZ_YE}XGZtWX<>|tit&J|`+32y@dSUK{(C{U
zVw9QnXQRwl>;%ppDT30+IzaLD^+AfdQLt6puD0qmEXauObG>~}ypT1K>Fv|z>2jTO
z(eC;ow@SL}DPP5Rr_}o@T<P7V&HFS|;oaHmeHmN1J>(p!*nDy(MJm-v{;j`4t>mAz
zE3+G&VcUqmN{d}GjC>oe*lyJmK@h6msa^?)t>m7m2vzlSdZi*#;e}o?_^Z8J!-_Dm
zUHRQVZDRFv3pVdL*pw>%y6?`k;HTlL?=}t`uLi#l)%;{vjs-uDt@vJP2-SOc@ek4v
zsrK$|FNnZj>)m>$F#p<>9#F$$hR~(cXOC~ejOefJZexY_O}LuVUFymEFjT+YMpf^-
z*osY@Yp6!MG1bd8QkC8MUhhkP9Ue`}oOb2SC_kCsZjt6iO6&$X>$C^o69?~p@$BT{
zJu3(AhN5?T<{l_Lr+dKTkqkr}!OGI)!oj=o&pj(s2k)Mmot(R8WeE?K=U*_pbk7Rj
zEle&h-?Os1k!qA^zrMPu2(m-V$)(2HX8DXy?^Aa-@d(gc!I8DCH>!I&#rJKEwb!h}
zO+ekt#Ah993F@9M{`1%d5l;hYzdrr<(WJk}BMr5aUaK?GT+}^ZeEyyYg5K$Fq8pMW
zxH+w11VfKTR#7Z}a4q(^J)&tqp0M_dX%};WA}2R>cM)HTb~9EF_}-VEVj5`+37F@7
z9Bu~*gt`5#XaioeN0Ae>Bkz0J5V!-?lYh56!@W;^ZQum=Q=~2j2Ah?oDf)N&Olodv
zJm!_9er2m)IqFww^{b5fRaX5fr+$@JzbYtSS!!-rYHnC+Zdht=SZZ!qYHnC+Zdht=
zSZZ!qYHrwSZrEyW*lKRrYHrwSZrEyW*lKRrYHrwSZrEyWIBIS<YHm1cZa8XgIBIS<
zYHm1cZa8XgIBIS<YHp;}+(@grkydjft>#8r&5g908)-E+(rRv`)!ay{xsg$GBctX<
zM$L_knj0B4H!^B&WYpZqsJW3*b0eeXMpn&@teP8HH8-+qZe-Qm$f~)KRdXY&=0;Y{
zjjWm*IW;$OYHsAz+{mf9kyCRcr{+dZ&5fLz8@V7ipwHmXv;SUb`;?5Fnmc(lck*iP
z<kj5CtGSa`b0@FnPF~HOyqY`tAb0%S$g8<gP;;Z8=0-uyje?pR1vNJcYHk$N+$gBI
zQBZTEpyY<7)=ja-tJ2{tQ^^f1ZK}Uga>Fu}+`uBS8ljRKmZ{{1WvaQM)=jZ!t0Y6M
zn_6n!)KcrFmRdKp)Visq)=e$7ZfdD@Q%kLzT58?YQtPIcS~s=Sx~ZksO)a%<YN>To
zORby2@l{#NYTeXQ>!y}kH-#&$IxlrgQR}9bS~s=Sx+z?B)nur-q1H_;wQdUMUu9lu
z-PBU+rf?uuBUJYrYTeXQ>!xr?R_3MFP2u99{z}aawQg#ubyG{Nn_6n!)KcrFaH3Wd
zujYnYH?`EdsioFUEwyfHsdZCJt(#hE-PBU+rj}YawbZ&PHZv%BsMbv_wQdUcb2UPB
zzoFJmEwyfHsdZDhx2wre%Z6GPwbZ&O9OhYGA^rEF7>k;v)=e$7ZfdD@Q%kLzT58?Y
zQtPIcS~s=Sx+#o`N_MMtQ%kLzT58?YQtPJJ(yAsyt&OR5Q%kLz!rx7m7rcAauav#2
zEp=0Jt+}@c>uzL;0xn}KTg_&3#j(?cTqgCxQl-4k++<dO7MqvI{pg5g9f5*kW*vlA
z$fs_ZUV%^I%1dS!=aImfUV$4{YQ5eBF+cjk9*FTRZ2)Au0VepS2w`46Djc=VwBmV2
z@jR<|o>M%}E1nnPpIfHlxvhAvpx;u^Zz<@v6!cpP`Yi?hwt{}!il^Up6weiO+6p>t
z1)Yw99!Eisqu{fn;IpIPv!kHjQPA(G=vVMLtteMoQLeP2TxkXW(~5GX73E4R%9U1>
zE3GJ3Mp3SeqFfn8xiX4!Wfb+8QIt2MC~rnl-i)HWSw*?BigINY^k)_HWEJ#eRrDzM
zpHtA8Q_z`H@IR-ZKd0b-PQm}2qP#gpd2@>L<`w12E6SBulq;{GKd+!aub@A#pg*so
zUr}ELMY#%!aupQiDk$p9R6SSl!Bp_URPe!6@WE8@0a3wX$MN>K8Pb0b@Repc-dl?D
zTZ;1Nag^&%dK~3^(BmlQgC0jYAF_)6NROkOKYASHeAs{Es2(?AtSB7~*$edilgqQ7
ze{w$Tz4>QI|9wonzMObI3d`H0KHh`nE#MU#?E69{H+d^&4Dwb?6y&X#AIMuVEs(cj
z^cA;q5+HAdt}kzeiZ5@2`AWl>d>hPHn!)7TV7}7mCEiL~m%J5ID0v$!pzKiO+h75u
z+AZEntChSB7Es!#<lA5YrA11<4Hi(^pXA$M0cC3!Z>5n*-Ui8#CMEecNQN{Z$+tl=
zq`64G4U!>^L*lKp3CY_a8PWnI-v(=6+I!^NV5Lawj(i)e6xnvfTWP|Px54U`1{?V{
zSYBzKk#B?Ll}e;|D{V0HHdtP1aglF><(2jo`8HTyX-$!DgXNXB6Y*A>Oyq5l3~3;d
zZ-ccj%^~t_kQ>tYA>RhMAx$0Pt+a5++aMXzo<XY^FX^HC^7bTn{_(zTF-zDNvvl*+
z)a1#Td-&1{uqW7Z;Ca9+b}htj<Opos@KD6;SWs`>jqW>W?^ftacVvrUWY2<nkEF;r
zf+D<gq3f23D2DbPp5C*Un-^qQ+^$<*BH8=)Fpy|<-L?`z5LqT8G`-V-k=4KBy!Xls
zjMGGedUKwtGQGBfuV=fpu?Qm+ZK3PVs0gB%7SVMxO$151T_ljI;0De}vq(^zK@8zm
zk%0UVf_9_mIrDIv=(?FGya_ebB-GotJvj;zAKM~&row9w5JvoY{=XOVjbT04YNUN6
z=&FL{@;8sJJ3PZoRkV&!tP5Xc2?cXw8b{aNUSV3=ZKLa_-j3Kvqv*P=&igdn8q~XR
zMVZ@Qwwpon?85S|-W46rE+qI}M{$qNScy-buP5qUw3Am#M5eoH4j##VVeZ<+cwZ@`
zxNB4L5X6YDdZ&B1e%f-|bx>Juluw<aAjj?9k&)fiT`Nc;krH)G1mCZ88yn?^9+Tsg
z&u)9NTf7M9`;LY4YgTI51nk95OHq6Pdd4FsQIK$RJoK;4BB@6D@-U{wrG=?lsj<#6
zWi5ok!#@`NgAnP~VHU@*13fJA*-+@=m<FYX#c3M~Jsi`Z^swl2L!pOb8k8OuQ*S8r
za7=^J!(#Fcg&vM+P<mJd!J*K@F%3u$k8(H+dIY9H=^0x4;!zt@gVHm!`r@%82c?I{
zogA#bnEKn+e;><xet~&Fe(`9SgV87G8A^Y|V{#6Ko}sjV9vO5f^spGCgUS<+PdW^G
z1f~Jy$6?V=he8j>G$=ho(?59B*8%HGM2sB*e<Y^C_#@)h4uzg!=tFQAhj+L+1bQT<
z!T2L$_zr;{iD^)Jh9%DqkF7kQJd1e7L*S3ZG#GzGROKPiBQYhSCo;?CvnV+WW5+<q
zoAwb8NxlkZPgdNB9hT=M<47kjdSuERLKMteG4oSW!-H85ff|8mP-<wj9SSuJ(SX#j
zkln+eh9eq`8XjAG2-FBfgHl6!T1RR^`tRd}af2H)lv2$jrzh+0lv2&3u6Lv^DK#`P
z8I+n~$q|nsKOi+c{`?SlBM=QHM?47rP^cL~P4P(lL!pL3`VUxB{5XIiP$LixR#W^;
zf}v0|gua;{j4%{xlGoHFetRxp1mfq;nds`=&^vh)1CDE=BY{(a1A=iJY2Ro6mWG>A
z`~izYE{*y`#+!#*n)VTnrv^kES(sLzrMoultm5lr=p|Cu%8Q0wqISK|mSfpAQa(6o
zJ4tY6QDA>^(kyV^Yxl!G`{IiabBD7*2PIl9o`EIrr0j?}7y9|WZxh$tTKvgTft4s9
z-i}fra0O4CzR~w}VydvR9e+G^KrQuQ?nqbYXsWSW_=h3=_woE>o&-oEM^^jJZw()W
zz2L)_r1%lsF$YxlTG0`lz9Y788k9bE`V5Zw@}I~3;sKF%JiPmc{*cF=^xbFSJ0A<)
zYjYO06FEb?*M<|o6FG4Fww}1g&J`0!dxqjktB#4jbyyk153aopOC9J}532LSDnY-4
zsh{eIdZXfd{}mg_BA)ZT>9ZZnpyqvVEwN)JZSF7kQK3n+jNWu&PHY=*2OVQ+AcA8x
z3_63Xx1NO2>pNCAeD3aI??fih342pG7>x?mrMJ<MVN$9a0dHYnx#QIhhAXUW1bo1{
zqG*p&)6+NO1V_|fZz@lzUa!nI)^Ymjt)$mZ;+Cx4y$wo)r0ls(`|-^AO3+=MDOa0y
ze&*owao})~A2Xj?bL%!gD;|Vx_K6TDuAOR>wu(n<_0~~fJX$|n+J+VaXRV`jq-&+L
zU0vHevel#~%~W$UK|5HYGoEWzS-HzoQxcrUnPY3@jJW15)A<K<F5ZbQl&i7L)Mn&a
zjK|8YVzpLvf$w<V_~uG?0?!LejWV5^$B65uBoT4Q6Mvrn?~lY6eXd-ct+@Qy%EYMS
zQJt(YK2C3zs#S)nTT@lDaG`|CpS=@jQZ$iQ5oe)Z)*JiU4eeHT_9r{k)1kvsaT4q_
z4imNTFBa3-6ZS6-prwO93-kns%(6p33C0n%?95S?(gX~<EB=L@oc)_&Z}aRc{%;mA
z7L4&P7RTs0j<LnRfa%*DrN`N~Y$1w0w*kXo&S3f$OOE&#3$qO7Uva9^EKP>}%gQ3Y
zN>dlmt%nUUyS2Ky09VhmtEIKmla)&C4jd=LkFppG9Ngn$yKF6O-p<KREO4e<%qN>}
zsnJ?>OReKLp&f^$R*Dw2vB2q>{4=+45~uhTx2vbCwL9pv<|};YQs@?5jvgnJByfb~
z(%PoGzFmP2>l!~qau&xvS0F-TSNSnTk};e})fw5sCR#HgI~0#>qugk=7HZ9A83#Mg
zl+Lnta8Q-@PIsNs$Xz(k?2wN2OETfL@d+!XXx;%9w+me<wOlXXz%i@sG}ZE{%@*bR
zBA1|JIFPf}U<FbTT5q`P=&b0Ineqm4sZ+voHCaLzXN5Lv4RIu-q7v(kat-I-fZr(+
ztUzzkE)fRrChJbdu8HSh!`*NjGz{R3QZWuF<e}kuW;Di<@py#dfb1n4*u)O!rIY5^
zk-7>3;E#NIMjk?on93;2mGPJ<B@EzpHf<x3B6~{21pC@srVXr!uz-Se?5aX6C_Otr
zm@-y~_#NfydhHI>b9rlfi-~2|wB@QlZJ-J<3a9XEx~&#YrA;kP-z+LRkb5ZAYN-i^
zo^jEQ*g51LsH0F?Y)+uV;;BH-@^dmanYpr7b2m23-jZ9rCCAyty%6IhOm6i?yGWjt
zc9A?O?IJm+{cs1q*RVALYFYJ^*D?Z~Td0<rIG7j*_EODLy$2dmBq_8-FJ<7|<Z#OE
z)zwl}xbD+X+DY`p?gEe6K;Irh%v(y)ngh<j=A9Ro8?Eh9rM<r>wM9*&R5{rY!*r8%
z5?wy3gBfK~X}Pn^qDgxd&U=fW+tHlB!uBUjFG0U>E*>KfrsL1kf1kkgJyBNSw#2nC
zJR_w{o*i$?!{2w}SowgirL3$aw^f|p##pk25<SCTza{8X)oO{INnb=k@IIh0iT8v6
zZ&~+`B!M27Wd9iNBq<3>y@J!t!x=$&+u~woo9V-BQ%qoBQrWMb=BIY8QYvpPxEQO~
zi*^QC1csqC?B_bo8rJpKvqc8P^hKJ$s&BP1SuN2iki3CB*^eET7w^U8p%>*=6ZtOT
z+;SZEZDoP>>A@6ex?mD@MEofB0GFZIpsl%n$^#t=H|{o<wpUwc>u!-Lxlkl(TR2s(
zfxH6alSs^lSsx8ZHtV}hd8)|5CPuqyoX$s+ekX5dGr3&K%x1^aSsbllrwZx(c*e}8
z^94JV%V((r3zV(N3e6abgU%yE59?CdFn(=24zYnSg^19aPLr+>=)T!f0L_uZsS6@?
zfN|Ie3PtnO+2XppQQEH1oHQUc!X(KB%Xe~pU^NcY{#vDW>MXRK0YfPxdwb#dG?xDx
zZc6rjDJFMh1>q_h<3&1^O{eWl+C5^DuuMh2s)wtk;N((y%<R%xv*6@Y89U3WCF_`}
zY=Kpa6TxC8$$UEIvq<jwLN;YP8J2slkV<DVJoii<;icK`v**R3sWsUE!Ud~4j%W$2
z{z_n_A|)t*Y$u<l666cHRHl&QB`_%<c7en?RCg!f;iIZMGo8-G*ByNo&D(ZTnKU&i
zvtVUX>9oc3PCF^It7zWjUL2cumYSoPEyU){zKZ0X$vY`KmuGorGO1jK%T2zJPFaN<
z<sIZYcWsU<ILq8|q|gTChJO~xJ!_h&Le}EBqqHzSu+AiJrEo+D<sOt9fhHg~-El-q
z5HB}Wf?VEImmo%NGT97-g~?3;(r%#%QV9xXE@fx(F?FYs8)Tlri2!DN-NnjH_pUf7
zv*i3AD>o=YI_DJP<%WF~skt-+B$KE92G*<``WQY86d*%5q#-IdeB(i!+_22kc2*QT
zC^!7GXzn>H<zVEY+#y0Z7>08_m!@#`QMnOl0&<h?iX$jDgd<*VIQw$8vIKE*1E%FI
zo0Y)HkgV_$IN6kyVG2&D?o@Jv%wac7OQ8*p6;8a|SlLvLaSvk!Ocof;narlM=($;&
z59Upq#A4+po61-P+i_xK7EKp_p8oqp(_~m8HJQoVC;;pG@&zlE&GH_gfLhHsc2th|
zhP603V#GioVrzka7L$7>mBmm=y*?UpKAje=7rAF+<Vc_i$WbQW4M$Lp2uHjeu@abO
zMp=S5Iile<FUF1>K-vGG1SaEOs4i7<M46`@#kiG@m7{Ei4HCq?f>TIka$@XA+bOi?
zXx~Shjbr5~hdu*GC&>mBlwtN&q~@|&G?ZM9_W)=Ej>Bp$Z-WQ97`fpao#W(&Wu7TW
zArAIG{If{zITHgEw(C>L^98hC&h@-$r?UA>wEq!k0&>$GM^J7EN4(sy64-@!$>*O%
zN|1F>0yb_zjknMhag8kxHQ3I?=o}f9+#vI8&av|`eE>u`UT(5^C>nh1$U<%;_pAaQ
zv*m=K%(Be}aaBh$iy2A$n9071DK`{4C|KwyaPVHr$riZWKxT0|O0>V>8&Z{-I|V7U
zLAl|dMRU(Mm?b$Zcl7k>w2&J+4b>w?PZnqba+B?fBPch7BVKM8`=EBm_BZ^qXbCJd
z?m~gJyF3n$5#xS7Yo$yxrp;$nazmM$Fnh$x4f`sRcLuYJoWtY>r>UX&i>W8Nf<i7v
z*J67D6jg@~DjhF3?5k+rOjwx40g+4P#Qdt@pyuNG8^RJlze2$=x3gvDLAl|dMRJE)
zn#!SxlD-3lJ!gwv9$d#+iqZZ?pb5xLcN{^vAskA%N!z)sx&(3K6egv#vPta@Z9eDl
zIZgp=MV}oVD{>v=CY^z*9V<8Ns|fcnIG~}kywP1_!Xfk>oOG14*(e|AZ&;88#W<Bt
zV}Vo_JTOl2uOfM8F^R)A0V=m$FjKjL7^l$JVWJl4Z$yBHIHASB=FdDJH{!Es?yw4^
zIoEBBj80ng@^%3_GwW#s^DBuaAUD~rI0ACRaKy_^*Am3Y4J=DyhG;oZH*7IC&YR#L
zw=9I}P9-<U+$m&CMcpaJDKd4jzRre{mrska0)v015Y??%FbpX-A^rD91NNOfv;+t9
z);JmFSRyrv<S^<GEhfflh!m6IjENbK!$*!_EvV;Pq0EDFq?m6xXexyaw~l0>c-c&j
z(1Am9;IoyW9L3GIy5oqJAYP8TmLNusU|L9#Uj+D<qgi5>*>^G~<~*p1NdIH8nNwUd
z3}qhd1LEgf4l;q7!t#ch6zB7j&Veme(4FG)rk+>IQ9y1WcQ}bHO=Bk6uY-BBuOf1j
zhC&Cyr~U`uVyxmaTu4KlEY|l0xz0z9xPpf=56TVyESkHSNtp#UZb4L2U_PH;VK~D$
z78$oh_?CL0|4Dbn5iLQy+;lBLoZJ*JSaFRlL#&i`4u})%tWmj9j$15qJ4@<fOx?xy
zKk2NUGFhvGNx(t_6x@ToAa2ZL!DV9QCIed2R=jPAe-*7c*a&c@D#<LGxt!ZUY+-H5
zN9}?luuYuYgfb7vjrc5@dlnW^7y~JH4BoJB@D_swhpZJHw<MaN+;qhekQ;_0UT(UU
zAVzK+%yIJY>Ou+9c}ROk^f$0A=VInpwxt@kkhz`DJF$8)|0>EolXOg$cLr(~DB#@7
zrc?P`)V9RJ3B}3{Ongu|a<StS|0<k!4rj=+ffD_V11qj0`kOSw$;n3N<sy_(oZN&m
z56VrvJqOwxOt4U~kb4@I`@Cqa4yn%tR`S3&C2|k+HwkbA<wkAK=~{xQ+~jcZI3%A&
zTIf3vA*iinn@eNTS8&+;G@$R;%rc{5Uk-zFoQ}c2iq;*b6Sl+h#$ISx<N5pw3S<i7
zWTdZSVV`2<26jAj?*-D)BYhqJDx5cz%xnrXTPk-R!)1<-&*=i03i!M-$aOw5jw^U5
z^Pt>_33Yh&EDgDVg3NNqaFciV>Q5m}MiQo_2jxbf3HCQ#aYRcH-`{jCK~!#Va4SYy
zwvL1Ie90a!<~X!4XR(nsC^yP^Im^7@#Ew&T{Jh-3#HL`84H7317oZWsh8xm<pG0dS
zSQ{wB(ZY6wE2=V|&Y8597L_CRRalN-wM|1{gwF1yvfM_H$-uT8XHOQP!V+X2lq1UA
zjI88kGC9olcniXDyJ!Wx*U!>y&xw&EfyOUK32+4Eh;S(7$j`oL38Hd@<tzvc*LQ5#
z#BzMj0}Fi`)AyMEC$Ac}AV>KOj(Lx-yEuI(Lo0dc=}3-jn(pwqG9F`&6O%U!h8ElZ
z6u@~i6K^BnUxnoc>9G*P=LZhT>Hs7i-NC%i;x>W+*F_+<IJpUB9^kt8ESfty8m#7_
z<nR!Ou_DJuY$t=39HZ|@G=8}Om%8DImLOhkx|SeDZfNl{Z8Lq56m~2MG5)1tF3j3d
zJ=rl;aszuXIto?Y#mP<9M4PvnON!~B?{o5Oj+1e6HsmHIZwg-)D>o2*%&rQtV<rol
z7isY@@8E#!JlS_lL=VGw&DO6VIv7y6O)|)J9y%>fZbF#{<%WM2ksAlbKof%%a<|EW
z0ZJc9twm#4%)DF#{tM_k+3q-^C5V@st|f@dO*Rd8q8!W#O!Cq0z)C6sn2ByY+TS=z
zJsFv!5y!4Yn*6H>_b@L`6);dT?m=4=GK*8tX-Pk#@37#4v2p{^hnmd#x`@9H|0<Gq
zL8uwXJB|5O0h45u8-pD)uT$PZuB-hnLYW8UM&WmnM;C{=D3v^&!OYU;>z**KODldr
z-w|OIgL2ayN3;a-a?`a0F>(V>jZ_+j6p|ZEIpi947Pg&2jBU=bRs9WhTUjd?JHO&z
zMe2?gsxi{DywTqjgwF>I^jHsx*adkI$XK}n8Z&LPHP^6yBfbjfjm2hks1Q_)ky)tB
zPM+!L=<A>}NBbL&C4L<ul(}DS<Y&>`VGSwpRaba?qzc%EM6K09L&8!HNv&UQJT(4s
zDwFSu!!I`+N4(s0EkRUn;Ie{B0@H|pm{UPtWhF>s2yvo*7sy>DH<USCX5w^H`YNRV
zJ{coUl$%%^FfB$)4mu9z$ef#)H-o%z|HH$8#>x@;D<~>#{X8m1WQ&dDou$PHlj-bs
zUZ_6Q|4_5Su7+^`BSVnJja#A2gL0&@Cu8*)<1A{yhBX8e7vBFApeJW}h`68}#oCj*
z;t0wS;fR-`t|f@dQP#$~1Uc%V1bNs}3w-QI7tsG?`O1oaz6JiN<cMXS%hBR}Ox?xm
zI}U^e!#&Fz+9Dhl+1NpAk?_9@%MA~*8Y?#z*5Mo|n=yHdufj5$g;Eb=7F*9MV8WZl
z(iR)HP#KQJdH_GyITls_6Uf{zH}bP+?oe`Urfpey%qDHF@1%23eVu&7zT=_s_dnTe
zR~*q2#LG>HebExc$W0dQF3(rw3UGb0V47$AgN?;bNBk~O0+rmb%=3Bj*$c~!_$tD^
z92Q#(Yz+x3l2}Dfb1O#<a6W3w=D~1d<p%z-m>4?J;l#fVA-)RdolC>rA;(q%umlSo
zUD$W95DuR+W?S-eonwiYn^5L{uFKD&xnnAeDJ=E+W(E~t@&1NpxK1JB=jEaC%S{gc
zNnLP6OAs$NT}u#^8}JWCIIbtd;|@9pmm4T?SQCnj6)1s9ZjgBvy;Pj9r}!$$Jxt;X
z*hdUq3%V8BIbVmcVe5gRDBR!hpvAFr0~xnXR6&f)imxJhL&{(mgr*9QZ00M(YtDq8
zoHrxm6vq-TH=)dfaucs7=L)dr*yJCG+->xBTu*l3DFh2aq`#552lV6wID&FRq{Pcj
z*Am3YO`gVi(%3QuY!85cAoo9@`IQs3#i0Z$xnY@O9V))=;>M|50Zuy1E(q>HZnC_u
zgVMxQkFebE;L@>j105SiXeZ96UVIgn8`%9IoOw3G!2%?PYA!c<j9HlSh4mX5*g8(X
zVVT2y6$PisonLO`XVKh|Wsam4wh6N0^Ytq@XR;2Ma`)>u9vXjtgJG&04!_)R9Px6)
zN??1-IMEVB<p%tNj=|?wm_CDNTyFAMLpJ$3j$gk)?jgAe>Az2*U4LwE3y4AIAM5)d
zutd2@zMhzB5I3Q#ia8HN86I@e^(?--E>@1vm87#+0*jGh@l`l)*prbr8#zo+Eyrt&
zC6KA;xFvS>CCS`h3-Yr_?ipG|gtHe)4%-0f#%zWF?-2|J%(CT|BM*&Vj$pd&hQlvM
z97ntyh1eG@K~#=1S+)wx?1EY91Gt|%Y`SoN=N<rlIYRCsIr8L&Wu9ifS7Cied==qd
z)<z3rdNR02eiwp!(ARU^XK&owl{7O8;ZoTJu?plK30ii%^nqNl=m={L<~BCkJ_HQ|
zXAUdB*bZXa=yBmVNo_xrtGdMTxkmb0$`zJR7%$OAkSqKXGgNHK6+W79KcQT&4V0Be
znOHHUEqZN*j*7{W28#(_5)vzTe2(YsJoU8}t=++U7mK`d%U38vRWV~SjFymPh^=f8
zA7n_YOeVa|S%$EYW0@gZLN@P>>(HYavZR3$I&hGIs|~F^j^|-%f#U%-2!N$9&tvBc
zrs^>nCNE)(pAcn;J{Jy=lp%SNV=gB$q}I-KydX<y<vC#;vZTQh<_cJJ#cG}HjOTMX
zS~r7-2)Y-?#v(K%m~}6q43n2IX2$?!2q#2x+eL=7BA0c@;(>>#L%2glSxV092}{V5
z21=O58c-It6gX@`{V{1N8RmMh6t2G*lrg%+WSG2!QQvl%p@Z#@GDDi`u?%St4rO?3
z5#4&S5_(C4CBz~lj5v_e{5Z^6Si3;Iku4p40PSwdvW4Gv9WpG*O6VmGX9(pNLW31n
zWQbLM^rU2r%t4`qlya;2m0nMCeEuYY=B%X<9N<>-wp6)V-)_lJDzS;IWT~t~xa>r!
zoJ6V8iBe?}rOGBsl}nT=pD0x!m@2-45-}Clxj;4E7F&(zt-MqegNoVjjv@p-3-!>x
z6|t|-226TOp{IO_ne<*2pFyZ@UmTyxSGO|80;j@%Cqit22u_3lT_+MkA`v7Zr2jru
zwgRkUdz@jh!9=zclZ&75KtvEp?K>8uiyb3D?-)r6w)-TDh4PY&CAN#%DMuf;CARzC
zb}Dm7D1&A8%IFhHiY`W7GiA%l=-sh0dUreynM~P<ghaBONIV>$EVIaJK05jN7P2nd
zcSL9Vj?o#<1jp9|SZFsNa(;e+MBct5<n6l-6%DJ4Cq-y7-2aiYIB?6_SNkn%U+vpY
z8TeWbFtTc5O_axKp?YGsEQ9u2mO=ZrQwF})Lm4mwj8}s8EziJv%QNt9J7wT&Xp{kS
z*=lFt-SP~)Tb_Y;+bM%UW5d$3<Oyeycgr*IZg~dYZKn);jg3mce5~6g@NRhq-Yw6-
zyX};Lubol`%&D@Sfp^O@@NRhq-fgE0d~FpOaCb{DgZ3@Upnc0SXy397;)g7sl#(tN
z-z%nl&71{a^j8#LU-V?zcS}_GZaWkcdY!*D^3`&$nC)9u%=RrSX8X2N2BGHei6;{^
ze*7lo$NA{J<$UyRJ7wTwB#Zv&XOUP&ks0`IDFfearwoG4ojWkKS!BxmIE%bn&LZ!&
zQwD+N4okMg$cH}fmNM|Yr3`$xoiYeDcMl_P?%pjcK|6a^g7$5v3<Axa+w;6G*LO=9
z_--i!-)*N1g3X<8w(#npealMFzGWq7-?9wihdiubdF_CdDDq6?`2BZW-IDKU$ZMx$
z$m5&3Pk_`-9V_@A1?X`2eztZIgO${N%Zuo}?ZCrKu&HzRQ7?n`E#qPPmSxbs?UX^F
zsSAsq%)oa`8Tf7~1K(|jO3L_}I%yj;63I$x-%%yC@3>wfQ+A@>tEG_sdkG>&qc`=2
zJ!&+*TPlt3mP+Hh?Nl0HvzMnU%F@7OEXqcuY2Q(4+IO9h`<gsyeKe*@<n22`-o7K`
z?YmCMea)Rdp@XQ}$mJbJF7G&UdDp2Pf=!+K{c-GQ7VTRmrtMoMrtMpnLHzs+TUfk?
zM$Iw4&H5^b62<o#Sznu_*@CPzK3kA?#EAAgVnq9{1DD}O<I#zZ#+yVD^7b7eZ{HE}
z_FX6BzP3vA_~oO0$0?WZIOX!L6Y@|yl^rHg&QD{b`XuD-J3`*RW60wx#wXb7+Vcsh
zI+0J%)QLjE(l$uRhM2&{=Oqmoq!5v=0M9cx6VVbw!J;*K?uvu0KopXIEl)q!vktVe
z_}54hpa{{N%!tNvqoh%qvDF4q02s|=G0!viPg>)o<#pnuKSLfJG7*zR(olwum4?+0
zRyt&exJj_K!VLs|CoFnNf$yXCGf86cF)0a(5Y0)Mgxq47gbXQ=U6#y?aBQYET(*9L
zs1bm(Ap<;`EYmj<GKpGKxK`kb439vt9~ol*3^^DOld#T+O?!5lIaURk#Ig98lmtbP
zNy_~b!Bq-Yyci}Sr~_8@xkDD#*RWR=3l@xKnAB~XNq-<iej2H2S>y&^L*ZRum`QLl
zg$*CeqAcbR7=~?ICbsrau5g|w3v`}wY9<Dik5h^9gsAT%Qz7bO*o9olZ<rQ=QOP;1
z;KCs)i@gh+dbB<)Oom&ZI5Jnov-V;cjRDbR2y{_-1jwOK6Ic~MCk2lufM|X2*~ID`
z_t^6bmc-&?R}vI{P60(HPK9WW;S@5ou}qppw~E+s<dKTa9h_#YhVmF|!3=4nPSpDN
zz#SGW8-B&`F~NQ#Y+1*06eO8#rN9ygg<xb$CqX8$3<JJ42~dP+PR^v*t#4Qh!JZGS
zzJO+OjmYz8sE$oh(edZSKf9FqyGA986oG_HV@bpPI@Z*%%aj(luvHQb6Rzy2B{-+k
ziW-kZ6=WL6;*%__0d_?ZWSVk|d$`Mj4iqC<$dDXnsiLXhfRM4y12jX*u(lgvQs*u!
zAj&*xh)LKx3-5S1Jda}!Jo$5wCm@)FArTJrd{HjIB!R`pq$DT;OtO^wn<7l&Nh36;
zY2Q1!5Lws<43|ab=ntAH{04V=@n^_Q(upKVBn@SVUAGj3iXufooY3^qErLlov^;c*
z(ZNq*@i8e0iV)37n;&wEktEtF4B}{+9B~NA^V%fo#~yTen8D{PEJ=J`mXJwt%?+W8
z(mc8?1Ogs)!m$gRmEjy%$V7Bak)gkjN`fNDq{Ph)xy3Qb!hSlokDO>ugGp?Q6=;S#
zQ`%+@DZvbBewmO-ENMuR;2F<0{b962Z$y5E%&h?99(Edu_-_8@CXU_qGbss*5Y5RY
zsSv{??7@LMd7gY&XfqHTrnql3LNB0_vyncDn?@2bNhFOh3E>gY5i!@&G`c7FmqK*O
zy&jMGMjk(tBo<$DLnx%KDEv$Uilj^uNh36yXjkYS(A*HM4jvTnuVgf1E0D#*D+DuS
z`f(DyiBDQXDgiua(O<)5mHf6H3l1s>tBefuS?q8`=4=KKWKvimPlO^wb8;pz-95-8
zWQg7qu4d$|=um_otmko>F-yf(wQ!%rm-`Y*QcQD0Y#I!2-0cGfK-$2^MmGe+Bd<yB
z`W0jn$I_OhL@0txVr?i{#l|!@$`I303o+o}Cc~N=lO)g#`tg#O)b=i1in<kM5zPp#
z2R=PuGbXVZ)6sB5v=59~Xume+b$c2Dn_o!45Y3)qX(hrl>rH2t5Xfx+M1F#t0r2<<
z0@s3K*l)?8a|{U)Q-9m~@6*x)AP%rB7e!PcEdb%1$x?tW3Xux1qnKM%u;ByqcoB&^
zz_NT4Phw&46EsM{`~+bO({NZ16WEXtWrl?)J1j(lG(11EG4Jl5NB-J}!6<^LA8|xz
z!w8<l;ut7dO#A2$(I5@PPZ6f_!zmgpazjEiEWySI+doBqF@isH1W{5k^5Yp+<zs3P
zp{Fk?!v3itAsSW#LA;3}Q8Y*c@mJa)c>E0w=9A%+4Es|1M-(VoOhZd5)`5}@(#`^i
zV%k|>a*c^l|19#UwnO4Hb`SSYj<3eB&7prh{$dSK@qTg!sdqn~L8{${rw2Vdr2jsh
zCdyDz<7XxVsoj412I(aIcn0Ys{dflH+I->^ryD2jM||WAq#671#7KAIUc`@QNa78f
z<WO4ekcu@d<vLEc?uX3->DE3e#F9#c&3#BA415tZiHqeUXGp>`kb3SD=7F^H_9D(w
zj4UT*a@$6=gl&rnm)5+b!Ae0;DcVAX;||BM9S#A{u>ym|1qWL=vE+xnPeDT-!;;K$
z=p~J22s;+y9Z-e{dyH7U?F?aYr3_!vwnFiPRK0Tq%@n+WQuSJc_E*6ONdHaXJfG~i
z?<VKJ$@_0QU}w(uBg4)-0!N0OIjN5fJKF^@GVHXxJ-S+@)-f{d;v9()NhIGsHY&bz
zPv=o#=bLXvhJB>+&i5H4q&=knKBL_uCG2;&w{*hXdd4mysE$ZNxeM*cXdh*dEOxw%
zh<4%BJtFL4E6Rwli)|t!!#>Jd6&plGM7!8{Fa+#v4{>o4J#cbY{ShG-j;@3E196lK
zkJJ$%=Uz&qK`uNHM}?eu?u`mL)B8t+e3U#F9#x~ETzDdl3OREr7!7ja`7$cx%wuF!
z$cMz1_N0F~#?IS^E5!1UTp^Z+<qENk5R}2`aDN^Qz%z(pzP(fcM1xTj(tj^IKAoDb
zHQW>s564IgL<88P0wo>9Al0@2EyF=;ICU6j?oDhV^OtNOgKq%QAg0j(qCu>70You&
zvc7mUBx~(50)R!UKdI%;U$TMBwE;vi?V~>y4bnjT6b)o+4Imn1oi2cASb{wySBPag
z95&L6Bo!lnXEdzJACfD?vNa@E2p}3(KRYB>2v9Ug1MyedAb9*@JtS9%Wot;T5I_{u
z(2|OEpk%`uK;YOOX=i=)jze;VSRSNH^U2VVTp^Z+<O;Ejm7@rslUBUHScBBNAI~7w
z?!(hf&kpIomnU@6ULHv8_R}{=C+WvC$T;H1Gf3Cwn*he?#(jv>K$@|ySlI6yk?zF3
zh#$|8#Cu4t5X(a<*07Z8INiD*HV>p*`=oF<t`N&Za)nqPlJE?qp8JG(NUjjeF<o`i
zrrvgixcLQ$(%NZ9U%Ohpq=6=lsJ7T=i4gEO?dFJuICt2shHVxIYnyg**iD<yvHkv`
z96R|!gmR>$(HybAEDh%imLm?MWoN`uj_mMjwju9|m6?TVY0E91m_IhTxH7-mTzb*U
zvb$9Wgvi^p1^YZc42_*vFEx;|<u;mAQ%e)Z82|`KZxoGIW4n6VI8?40({rWM?ul}<
z)l9YSY)u%59&+6QqlToVO2ufnXUgmoe1mw!mUY0Wm)1^~PPw612mxUns+5{7V{Nlk
z#cQAzDS>WvyIg4*$horSVwZ}sde*4aHcAb8zHXFS#>v_l!_FF5%5n_ToN&?;mg!~H
zG`5@W`h;=7FcwOUt(hs~5a_~LK%fW6joFI3<yKpZZnIviHr>$M6LT+`u5E20^Waxg
zmD+0PQA|3)&ButVvD9+wsd~d*FR!&ybG3E1lA3Z$TdCP<X|>`OmyzzEDh(ws&deU0
zy!iyByDTQ%Qf<4j=89Os&x%QKbJLv$gR5@kSi^nAwp(30TU>5zyTEsOAo+Y9HArFP
ziwldhGsmYXM>CjWyM?&Ku+LStx2nat`I*@hD^s(RbBnXbR@ziPd8q|F?y0k>sZ!Ie
zl&hfb@HIg8RPV*~CWL9Je3x59JE4u<mwK;`Q|M!n!_4IJBwk+Py(DT-RKZPh^l4{C
zhbAnNAQf+=7h0ly^1g>98dZhA1K-3y=l>9Y(bl3RTr?*w;kwV(eYVy@v=*YZ5Up1S
zG*hitGlg60)%$Fwc-azDf7|gN4BXSyrI+dBb*iSorjL`0i<2*2IW{@HJiiFUUFVSF
zG1%hfkjF}uX0l0SCV+9i!z409YkP9H$7Yf<3kwVT$vBdka57Qj2n-=}32i($(2wjU
zovb{DwBGb&=P`P>^w<-b`~eU%Ie)BDI(5oz^imUhqX^D~X;Trnacck3k1AmFdv=9n
zMc7+XWap>~PLNe`sWn%sCMyHCcJ?Jk8O!Wo@$9Q5Kn+A0$S|3ddV3GGV>3(Ca~o<h
z`Vn?`&(Lp8q^$#DxPGL#Ua{3ISBocbY&Z<-D@&6LFmJ9bO_^4DW&2EOjz)W9t6AG_
zbxw0(lN&k}hD<&DL>C$K@RNM4WG@XrXc74H;{RR=C8OuHA3YrPqnPR8D6oKNPdqG#
zqi1#=j@Ik-Iv=;Xj7fUAST7gr<znpsah+6&wFL`$&7QSj>F#>Ry6ev7S*HHB_1~*P
zCQxmzWeXc*LDr5x+LcYavT5hcJ>{G^dt!EO_T*l7&TLbu6%y@ktlf>ZyYZfJpbzQ4
zS4SQ1k)l_Rh|Ut;?hU(Y*SqLY-=<Rc*}Bi}Jc;ae(l~Un&YW{DKH5OCV+NAWy>)wI
zQP<vj_^xYu|C8R)*N;KFvq-mx_B}1qZ8Eh6GBOP$r2ih=H=9<6tC0-9>((lX-jU7s
zdk=jA*@H?lbWK}pBw8ccRgGj9C0Z*aIUO~_i<&W91!U-At@V#Y`iIbeAEH5;;y?I;
z$7osiWWDozvIl)+_<FWhNVGz-s|v|(inP{9v_{g7<2783WcYHeRgy$1iB>`OSCG2o
zVl5P<6%qgL$zEW&U5hVpCS?DmIC(mB-QEpd*B<b@F5T?nj8ag!hvZy~bugBw7dasm
zP;_K5Mu)4L3|+P-`f?!A0<MGT4n2tO@b&I&V&RqwiVUu_2Q(DjbMJD`-P!ingF>>K
zf-N<Yh+v2G-$OcR^usm``fyd0;Y<6o6L;f&f83Rb$nS9xXQu=lf&c3&!qgvGE;UbQ
z%tEI3(EnyT<Xku;TqfHg5LQh1m-X_?PrF+sFYbRv#Qo1B4%tLH9Sv<KL#rK86-FEf
zU}h+slsH3K9tV)@BTs`*mOOOiK(dc$HjpeO52BSMtr*XH*`D8d1nOb+Y3S32WSZ6T
z?9#G4pkTjE8B_w?>p>j4HSz;Mc#GGq5#4}pjXj^JF#Og?2H(>8V`D!Jt1rP1UC7}>
zfZu>;^qk<Qi!=nkSERWP@C_mj!C!n7PfzfgNOKtQc_IzLZxm?=zD%Tf7T}NLM|lza
zVv&Ym>(TuAIAHpF9nui|z$3=Sen!9_1#R~Vc<~~h{s_`sh%~RqbAtaKIO$u0KMtI?
z0!{&*!0)93{u<H~9(w+9fs^2$1D*Hb`BA{H!H@6|{8EHnlyK&Pv9X^Q&;L%)Kqt$*
z9yqC71RqA4HwpMJk%nj^o_`b1FUFPNvslX}_z-?i1%3&?Tcn}qjz~j%{tMuzGz71R
zGz5PO_~|+E`E?=<!Iz3O1iwe5AwJKFGz340veUN&e;j2ecpN_}tAtO1pAvo!_$lH4
zDQF`;ze&(f@RrDz;HQXuiMPKXa1y*Fa1uNta1#7Xfs=UtI6)`D-w^2u{+vipe7+Mu
zqKDw8h%^L$Qlzl}SMj4X&@=Jx*ceFF@GkJ-8su39^HIf2{{IbfBH^C`KrqSt)gleS
z9~5Z_ey&JEGXJ-b4N6b&X_1ED??K+^`C<ItEYcACOp%7*4~sM;^EZh!1V4nfK;IJl
z8MFn0$MJhJ<W|B>$e4t0gp3hPa!!8|euto+o}U%@5`2}&m*o7N0w=*|1x|t&1Wtk}
z<SXGMnSYv~li=@)^aTHnNKZ2VllW13g0B*32&VQz&mZo8*MI>{)XhJG_XM8@zDW3u
zU=YD1XGcUDf<KP>lh2QfG$d!=Kz-12g11E)f*&c;90vR=A`QVuMH+&C4{|`?lAPU&
zG(QmV_aJ|H0sjT$kDinK{R*!1oZx#<uM%Dc{gj4e3)y<`Z-9PEL(gA>d?h@Nd?`K2
z*86a!=LEk-;3W7Zz$u?U8%PNzIXehCCH()1^z{7y7BrBY{S1DDpWtz%k?>!OG$-d5
z=O#~#je(>w4evZ3nwv-8^QOXgCLe#~!uQhkxtYKG4Z6N${hRNn>%Egd_#j<>ul2eQ
z(e=yUoA?-Aum8%we3Gv9;`N`R>qkEOxj&@q<n4EVhOXbd>6L#&*Xv&T`G2PCb2Arw
z`R%wae(VQ-N7o}Cy6C_=a9w})W1c|Q`yYDnN9p>uhu(b!U2pj6UtUSqZ@u#Vak@VK
z$oo@tz47AI^>nR1@w{2O{_>~4Hpid;ubb)mzyAE+Z=>s5-||;3U4OMceHUGS`O|m)
z1YO@b`;vR<`k5EZ{~TQ*cKGw+|6Uh4CQ{pql2~<k5cp&%wXFyzajK3X(YB&oM%#+C
zt*DY3?YW!Ua+~xBW!)NeYt*f=HQ;H_!?)ZHM^g>yzlXQ<DraETIj~TxHCL^<@&mYP
z@jE&6SgFTKJyxEpW2GJ&gC1FWY#bZY^T_=@j|}O*&l1jt0nyg}dG&_7UWV?s-_AML
zt#g03PTd;$cHGCHo5CXM7+i(jdP=v(s@p6cz6K0=s<(xDdULyadZ~PuTeNWKEne+L
zko~=C<<Xj})?7=ia;;iiSe%_XK1~P4=&@0cjTv%J!ry^E!#`J2{Idf%l73Dcag=O1
zU;DCyciNYI5BRbVXGLT$jg^Ka0)MX?8|8}I)U9(0138<97WKZbW%6FT#M6LjZm`>j
zRN9ue88W=-atNU!a$?`5IpL%y(s?hdrm@|0*W*Ih+ZJuG3=L6U%oG0B8}CBT1F=we
zQoaBfNQWw*nVFuUwHhUMR_d<BvJy3c%h-AKQlk`mRHdGwsW{L!d7hi2&X{j6dAqD6
zEzp1e0smY``Y*2MPIJx$%tf7CPVN1=Ke^R_*{Ap50D7DXh{j-c{a^@YAFi95$^(<T
zhOfIkK;V4c#qbg|VC?6(i=vysrNdaIW!u<w9V~8Q&*mUO*)EEZo?5JF^rSznf~9QQ
z%Fthcmvd7EBiR_{64uT)&U40S&O+QmI$2w7?|)0_iIZ-%)M-%<j^8NE8gY@);)YZi
zxNHbxYVwJYHxH7zpW`7g^+YHz&+tuNX4?lVBn?2e3zD5|Kw6wVrUkoJHH@NR59P#=
zJ#2}VUH32*ts!*5y$kB>R=sr=W6*x-fmMc89V)MFZryY9nvni`6oE$D)MDwV=Ph4*
zwE6X+0qwW;r_E*BT&7y%@GC&VRJUJ-6)o1gCD!fO_B(HAO1`)uL)&Yy&h-}6yHeXI
zH7MHmx)<1c53BXPSYmpLWWV#Wtq<EVu_~b0LcYTWnUMZ#)=O)rOQ+m()T2rpXt<9S
z*}Sx?V5zY+Gv&?q_Mxj_TS3{=f-N-^ZBo}hu=2RHo>Y`wkZyh0&h%XAw5zvBWv<(E
zHp_u+l)cO)lCFL2{4^rjib9uwsv-T?DmC3oS#K|HiG4=~7B9q<08^Iza5R=~t+4?D
zXkok9)))75YnA%qKC@`)^*<E{+AC@g@7CFq(!29bG?~uJ2Gqa_le(RT_d4$TLjxY(
z?KG**y9@KuKGyL&nq5NrucL$NR8P5D>vfaG{@51>#J08Gz1?1=+S{?c>h`JIXRX@Y
zU+uFey>}NH>Gc60aFq{HRPo(!EwxWqqI^5jMo7`k<a#_wJ?L{W<E|No?GNf<W)lSU
z$mcr+_26%#K|T2Mz=l%(C=}F#9zrXk-7h#Bk6pXDQXfOQA-G5fvxyNyWOI&tt?js|
z(OIxRfpZM$EM9`Ix`3ZaOYjA#|I6^)0sQ0mN%)aChw&Y_Pvdt9&NL)AgCCu(C}A7t
z8{UumEPgXMdr`v2AX^0I@Ov@xA~=uVY0x0yE#ymZ0ly!^`HuwCImJH<S|ofQXdw96
z_`Mr65PUs;zm1=Se@D<z#IrvUG&~0|{ZSf%A0g5_7x0rr8iJo9=zJdFc|qqS;F6$|
z;59+#6yR+^=QQB|Ea)WoUP0#!;9nBFBKUWJllVz|ejJ`(j4Qz(13ePH8Z;10{C^wL
zO88yCNigyMW8z!l|L5_e=LG+mz(f51D#}IA34T!EB>w*^XrSlB{|iMLf)9u^#Q$fA
zG{pZD%1-GCHc@thiT_h5JHf>NWkDywFBEhV|2GAl#B<_3r6>69f==T32L%lTe^}7)
zJiz}XXqW{213?49bigO!A^ty6q#>Tuxt#Qz;A;e(#PeqhItivTV%fJno_~CDW?^9g
z-viZ{hWq1#|IBHU`MdB#RWkX18stR6pGF-?_$UB^sV)2lWK6>U6*5LJwS`ZMZ%O_i
zz>l62{AGcM<o{ccF?vq&Pk$1oet=+-f9eM$O#J}C)D{jw#t0_)pMZ=>_<G0~!PFK`
zLNEv>`M+JzN${$mliET{&`I+DUi>IM!T*<_f#m=91PuiLJ`zxx=i&G7f(DZR10oH<
z7mGC17A_ZQrU9FRPJ(SgC&~W}f=+-B>pzmepTv*O<t6wa>PEs}Kz-2pzVy5R{t`^`
z_YquaOd$AoP_G1&{Cxr65==7pAbt}5I`9xo^7n73GlEI}s7@t(u}DMmcezMIuz^IB
zhUCx2m0*&;8PuzUkD*=(Ci%Ms^-3_w+{*=>1Xl!|B!90Gbdt<{7(Yr+@J9s=By*n;
zG&~P5{ZX3d;Y#g<;7P#LP9#k2gkX|CY9|Cw1EzK&VQMD?&*1q@pmP)S=`)Xa{>)=P
z&zB#(=dE-O@b};Qm3Pwht#A42na56iXY$GqeEQ>b{imBg@<&g^b^AW!vvfWBw_~5D
z>uWysj6bLA_=kS;ALx4blU7e$hU;(M@W47<ul&HFO}hTW_n+IO>kA)u)7$C#tyg~e
zy>z|pjg>#9>vIl#{EKuw{fYX2r|TQu{NMkbt~0M2`#*GboPWIVa$N77{KQ3cU3kMI
z574#x#4Dam*O$KW&Zp4z-<Q5|8C`Steq-edTz~4D|8hHBCvX4bCSAWWfBXzxU;c@&
zvFrAIe|{(3Llp7noqwF};{D5KOsaT_{H>mP?Qjr!QpqL^oJiJ0fGneE^f}GXnBLqw
zG|f*Z^IKf6)$NR(s%|t&scOYoJ!`Z|TU#}6cSV8E^sV%SiT@EP)JXHNw#FT9!!_2+
z8ymP*TgFDCwq>+7DV}e=)-1Pbjk6wUo@Y8fpIb^+Y%|5efT~>Xso;imbLpw*M%yK(
zTjBGf@R<oKKVfGMAj`@5W0lgWQ*I+!Dfr2A#@;D~V^S#=m>cyzEke3LMeus8WTn`1
z;$+&a6ttgdsWn%sCM(7O@|3a64tuToYZbW(Cr8B~3tiFz*klOYD-8k#$Wj5gJuz7k
zoRqVdijbYiWJoKAVmbyzm0d4dqo7b*>#L+XTWhIiZM(7N;`W49uuRKdS(;qHi7P8h
zQ>K+(**=q6uh;9Txuv7c(`QrbtN7?~YpK@Rx?4O|Dp%J^6+Ah()ht)LAwarEZy~o<
zF$M6VCf~ENb{lSpKKk<onbU({m$84(^;&yk5ae>?>AxAi{f)Ir&)c=9p<NG(h}2rC
zgw&|a&g)ouP}~dkpa(@gC{~SbJ*b`;I)<fZhI>bs&rj%?VP6v~Ju@t|jFYu9hV3)>
z?BPK$r2l3+8t{BY(lbSEE~P*rAw9pdrMN%CkT#bpLdER-oJtRhdQdDy9I*Rq)_ZtR
z9NHk5>djn=!C2c$^@3qUf7B!3$VNb+|2~B4d3638jOg8r6+SIkPZRrGf78>%{WVS0
zBVwzWYTenIF!-u@hyF-}OX+E1lz6F08%njIl!Ax$H4Pcf<Xq1Q`>G5*0_qX4n+FWJ
z(2RCYXbAoH+x$9~Z=e6B9cr{gO>P(bHnanccA#0?9HNy`dzB*oTAA$)z~%rPZ@JU!
z23^+*wV5<d;Ps@?=+zpxCtOOk3+X;Gi-z>yLU_+oz}Tx@O0`RAC^%eyYoB^V>}$K7
z9uf73IF)Q;)c~8@w3#%pYgdnewCJOE8kU;zhXUvk@U(HL*E60+={(A!>Nz5+UST3_
zPM8_27{%|k59z-X=i1);5Hp-CF$3J#UwiLkN|C2h?4!N+F-2e%vA_1-hf9%}NLy49
zC-&Fg`)Dz27D8a(?7fc`z)J74z4zfFq-o0$x?|<udu=7{*Gd{<b>|ULkANN2nZ8^d
zhB2WERUwVzdIa1JYh1V@_W1}H(tn?AT8?2BCY;QK9uY$>j3Zw+42zZ?5l3bv4Y68}
zh<Z-A3qe~#Qs_<2^_;NB${2<-X}B`<2)Of6jKX!GN5GvL0W;YNr=YZwp1X6xkp6o;
z*M7B?w8I9UQHF{QcL1^?^oXcOME^<L$rjzh(u4?8+DaOXP>TLfZ@K4N?nBb@a2*l#
z2)L^o3`0_==Y;+4_SADiY%mN<Q)C1T>A%GUhbZiF%<gcJ^c+#o5l0b*Ei7kpDxy7S
zc6d9Lsv`89P|peVoDgfZdIZ!XpdJBLA(~=_51;3Z(0}RZ+^Z@6*}m9a5mO}XOWMyd
zNP3Q#WR9rkgxX5#on#p5d$g5Qd(QMY4H=O;tR4aN2-s(6?ogiw=ht&?op8*Sp1ncO
z@sRzOj{epgFhY*Moja@~oAN^frUy+uXzD?8Xq~m*g19#}!klx1CXIxF<A3!Q#35}#
z)FYrC0r$iRDD>Yy#`RjV|MKVvdTFu8%F%{YZAjhyXx{rOc(<Mtwqx{oUXI$iMmyIa
zmXtU{hPi(BVPAm(PtOVUoUpG@rJ-(l-#XW%svC_`s#-BteIb3rh09I|{a2oUF}V*;
zy$BUTPbKwKvd7s<XM=u!d&{JIu%0^hSQ&cisHcv4>gXMs(1*IKM?gIS_Sos_5m3bV
z4(Y#BF+1k;R8mhRAJ$VzJty4TCn1I=#M+qJk0YcW5#^~DVR6#N)HAzsB4xB5w1drx
zj$V_olbCjs&Iv>M?{veh*P7*4t#S5%T!m>G+f8?U!k|gk+)}a)a%RfvH*Uu$J7T@O
zsFxS@@}k@cpd*~>2&e7w6eGc-x0<!>Rxt(eqGrpjH;cEf-G&?TgQdUow1bTv0eA8w
z?XU_Q+`E)a71`yJ5Mzp<cPjQXK_AUJA@A4@NdJc!ed<MM^P4{PLXU`gPFQZXnyJ>E
zEjkKyuZ0KoMYGW(A}sYr(Xewn-fd|sskV|1X-BJ$=O#^$Azh@iX;6F480h`hYxN!?
zp=Cn)?=kI3qdjSc)Q#y8QICj25~Z#qqP3QS>9VoruCFHXB~7ibqT}0<gD<A05-yqX
ziA9HVcf_WsQICmEZo)CK)MptR<%-)h?r<Bfv0mQTz_r>+b`q34ncX;q^`;cl#%Vcl
zw&HF9$D-S;*Q!l7^w#s74?U+txkHbpYg=0w>-^ZsJdNK);%Y3xex0f}-1YKWD>YYJ
zcPptWx3ra-t(I0RZt)n>T^5sOiAfO*-(>f`Jdj|%4mW^SxmGPMEY8jxpI%0aU^esH
zt@?H=HC?N0Z&izP^E0z2kmkutEqv#mI-8m*HQh?N>NbmqufdC_dM~Cox2va@%6GX%
z%QQ{Ay3~7hyb6>8<ILppBwk+Py(IcTt0*Rs+|F#dyyY&I&;WT^v<+XcV($qzmc3=e
zr+(lCj~pAjAJ-f3`vtTU3BL=+(G&c9{GN`Eh2Z1(-S=n?-w60I7jXCmfM4?{4pXE5
z{v$X%2l$x4N$`h|*JZdT_-*)B!aoiC1fRt7n=j<w&IA5D(tHTl1^ixe{@B=ualHw@
ze*hkW7x6oTyeQ2QevbqW3BMZj6HFb|V}YOGoAG-q=qLDv_+2aTzX<S0KtDad1;0t)
zC-}wqeL>(Pcphnp&X)kbL8Q4A@L%H@(eqOLXl$i#34Rc?QTp2ee_7yp8Q@L)D9tha
zs4N%bO8j?_o?zlRm07|c1N{UO|4s0RVB-I;gMNaE{|5L&@Cp2WQQ#y#KZtzkIl+Gh
z`X&5v@Ik`w0j~%q{(nc{C;tDKNJF$dR^&ze|0RKE5%3|A7xDk2A}`|k6n=!0;NKVY
z6aP;L{KWqU1Ww}rhM=GL|6PHTc>Zd^6M`!u4e|ebA`S8XKZ`WP^Ct-!2p$u3693;I
z(h&cjCeoZF=U&_c?bxVcs6xmEGK!xH9yJYv;P=y<1W%ImGvLMFL&hL}tS$V%U=G35
z7M_dxBAD6&)un_#4Ot?X+QRjaC4z6n?<0^Uf~hTxqe2Njf#0tRoFxAbq2B2^!C!`K
zNcb~?273NsJf!E;7JeY`Q(JhQNJH{+smP1k!g~ZBY71$R7uED1h`dPt2^Zlc_)i4=
z)D~_L_^B;?UErj)&=B-fTlk^CN%H@C!4rb-6ltg}oG)mgws4<FLp(DC4Fq2-(okD?
zi%0|TVf{CQ(*G0cbsoGw2KWcy^=nZlB!9=jqn7}FKHv@fJ}TfZp#BIZ`8$Ry!8Zc_
zBybQ+@|Q*35KQv-eu0x@>wBn=-@%XIucJOB{1>1>!oLSWAsR?#E);o@{QW%A5KfZ6
zt3_TUe;))MdQS599OOkX$={zKAA(8dNRA}@MS+v#Zx!$9ImzEc0w>AeD+T@Z-9-XF
z$=ol1CqyT~KOxeP{5@WzA^E!>X$U9D++k5(f}bMNko>(%q&YdiI5!C<A?q;>?>rwq
z(D>I^T`}>U$r~?jq;JD@`@X+9M%TZZyWswp;ri^a{Oq67^+k_+@<VjJ_z|D~4qeaw
z<IMRhxK_S#*&<y(bpKM7u3x<Kn_r~s6K*O02VFn%*_V#pj_doLaeMQXxW4r*_qFKy
zrb{k7L)S0f`ItNCI(d8XZo0nhq0juy{kZ<tci->;UEf?y{|~x;>T_TJ9$gC`x#d6T
z`uRWo)Y#i_-Tc|vak?J((g!E`^>1&Z>!*#+zKpJ)|I<&b(Des@eQ=eoPkc>%gRX!0
zFTb_)c3dHL`1AZ9ZZP_=qI(-oycvA5b;v#DB&_X~+FrR+_R4({Nc6<~vB||FGYigI
zsd8kw)I6QBP18687wYM`(rNcZ*exR7dYR2Q#-VDh<xa4j1afh&X>69x_)Uw_Hyx(J
zcv~k!R4FxE#z0|6(*_HMZdntV^n?jpr^rpc(IGh>I`?TP0L-8=zmO%jbLo1BO5!V@
zGm&N|UdL{HNM5SWPA|JrD&|AEP-<+=O!)_!{ahqgQA;gmVwNWI{>s)(2<;9+Xl0B;
z$|lyA5T>08D@*IjvFbtc8tYc7oo`wWD^+cy)L3fGm8$(!R=2jbOO+K`tLd+7d$h`E
zMM)GWr2jsj?etDAlUB;^u+J@<+4;(2y$D|)>#xo_AMf0`NNIZ%org(=Bg~`OB$=-J
zF(!L2GDRMyDpe}usVhCH4~eIBS)m-66J~ZIo%M3t3FQc78@X~6D4&^A`aAVnUq+`I
z+u>3bs8pj=j&{7FxB{^w%2B7X4NE!l31Ma;quvjcTnM|3w4F*t)3afye8Ez&rgdV(
z%2B6cjj*zIL^<kIw$W4>(`S9k(U6s)P6ZlGWw8Q{QW@%0q+uyTfo7G?4_O&v3b1GY
zJ-#zeap8zk$@{rZUUJ!7Zco{nWW`w%wvGQWCfft})Zz1B^o`_9+hc<RJb<*`)xnCo
zhg3<ecXhFQ@3E!@ZBpx9`>x8g-W6U>>Sl!6|9aPsY(CI>R=2$iO#N;9KisJ8T?v<Z
z_mGleYI|3QmH9z5l+nz}wY{s08%2L@YlM}h?OpLcFP#_vIw>imsVr^pvh0<m$pvhp
zTUnYit@Iv=7n|Hvvp?-ynaqS$fPrd|J)_#b73)&h-&l}%eITU&zMxC=2x-z}tHWCF
z>f{lr^{$>A6nbzi^9iB0cXfBo9Yhcl_an*s4OE%7cWHaq*x13XX1UreS_j6b0iqIX
zy=%*8ZMx`)dx+|voN0TkXVL45op)!9Sd~mZ1Z0In_#W`*3hBQmM&Svi?Ood56**}j
z3%9==^>)>ISIo9Gz0RnxmTA4qSUqdB0%xMhc_;P_YI~Pt2Db!he=hCM<(}~!4bpjd
z8_nv6*0XxovqJjs91EVlYn7+h8TC3Ng7<fpzy0A?zn}fP^45_2=n0W>XHm~v65&LT
zEs54nx@hHcJ2*+>`vlC<Z%N$wQdvwv)LRncgQs+{qjs#WVXIPH2rUxA5yxxTdHcss
zxW?~%W$`^4T6OA6bqeXfCp&ca!)|GqIZK<I-lDkcE?WrJ+F!hkjviZN<Q-`&tAH8C
zh7VRamX!<ya73}J#{=|3<Bkc?pXeKJlt`4Cb{8i~$eTohq-en`CWyEOBl7FO|BN}*
z%lQuP)v}ImJ~Ut^kTjo0lqj{9b%@<=A9A5(UCa6!PF#679_XSN^8M?eG&1<&|IP<a
zIaS1<(&iy?$S49%beJy;+jml%hdS(X>lQ_Ehm8$8>EYB-2!}ms)#jlt(FM8);?7r=
zHV<`*TCI2RHF2UHKQlY4SJq06(w5tD8_f|#PS~TWQ+mQKOjvt2LRH6Jb09q{r2j5#
z)r?Zhm~qz(Lz>pK-qpX}HM)vZRTTE2LMPt|&+?9C+qZgG$5IW{)RZ;G{kBFMDYRp;
zMp9Wjr+0NM+eoU+X6wTvixk?iK%=QFCW9kfhU-|QVe4IN#In*dQYem1W2*p9SW9iO
zgOFEo5OQ(3vF+l;!#I|@R>p?!jxw6=M*Bqd<6hpe&k+?B(tmG?KU%4e&H20!(#lxp
zW3vYF{2ED9v$QhS#lW}cs!S_mk<;k9Z0qaTG47Bu_QZfPcC1usYHe&dhXfAS>HOhq
z8dJ^JZLN)sN*mMKmz}C^G)k#zrPKCXI!<9o9_k_ecd^rHZXJgWt&Q24q?M;lV|_14
zc9cPF8cWuBL``E_8<U4Gi=|*~8`HKi&o7{3r!$&?Z`At*ccDny7N%`s(J)XQ1#vV3
zIy;P8Y<~#pze^)Mc)Vj-N79dI+t_}zjp>ETU@RT2jA>;|D`RV$rRpiy!0r{X$CcS?
zdX6HZ7ba&G1`Zm&^CP)lm@Mgq$v}j;jxv~3a}Mdh%j*fF81r64dyN&rz|>l*S=(-`
zx$CRl!nq%9o<55&bZ{&LIn(Quo=!8Gxu0IA)a#VNiPTVrjGl&wVXaf@b;)2&CLP~<
zsw}-Osn;djyCFMv7CYo6HgZc?NdLXLRKb(|b25*%jA_f5r$u&bup?11M;_L67mB2p
zDfKeto?E8W%aW(v6Xj;h3(wRsZbiplV>FtxwuEU*SZVEa>6F_vHcMw*W4*kw;WpfA
z%R2=YQ&>eP)LpcMh4kMS)|%y3t#Q^PwP|cO-Sr8Bf(p#_86kG>?HJOQF>M(O+EaFZ
z$k3LtPR16qZ-d`x#&^HZm{!KLGNwMd@?5cuX-im)zfI5HHhTNy-i{;|(tlqxGzTl~
zz~3Qi+8{H6k&N$J8Pm#GsYk2dNG$5wVP8A!YacOhNoyA^W7-mSY;y5P5?|>-LVk~=
z6V=O-rKHMNNdLX1$1zYVW66U)=*VK)fq(c8{JlB&>19g2Ot~MIDYY`Dl`%tc99PGs
zyV2;E+DGhM@evE@zb`h5MuJo0JU1l0Oi3;;o{NhJo-~q1r!8aJGNvtK+A_9N-HrAU
z(>`L_N6aw}RZ7hv$K}<Qusvf53+cZv=_&lMJ^;zLCt)AN+#mUF%2D}@jph)0G8`y8
z@;lC85<7wA4BNCE!?q@z%!Jv_&2y?ZjD=ETYi8<*V$O-6v?)Z&$mPfzRzxh6-lvJa
zIWhMleyCTX)YP66B}|MH-OG9ST_mnJm(Z=J>J4|jyw*z1vBS8ga2Qu=wpv=PxW#3p
zyDTQn5(}@Q53%oTb>Zu<3M-l3DXe!2dy~gqFqvw9$fQg1dI=swUdI8r@8j5_6AuhH
z_&e}zejU3NM_*F}3~dEYweD<97=t+Kj-(2;6<Aw=wH4SK$hCi@$BNN(H`)rUt-#s}
ztgXPw-8}c#PGPS1diw86dvYh#%GiEX#`IZV`mC=+`#SdC<WFm3S{u{anAXP5Rc%ab
zUxvZg7AK4|eVAf<UxN?nzqhr+AL}?SSgXa7>)LJL5iR1<-+A>?qqOC=P&ax_Ua!fo
zxq3}rugU9xk%^{LdYgP;_v=XP^_Je5)ww^~b8~uquHCMlYsb|(&;I+ea;0TpNli~g
zc6uVBCmyx!Rxt&6yr|iE;xV(Zu+ZQ1P&-KWF{j>hBZjT5z}gC|t-#s}tgXPdJ)zg&
z2U>#<>A$y!Lm6l*uvW$lBg;2(Svs2c-j3$2qjzs@)r?Zhm~q1ur7dH7X3urPJ=WSX
zwyTygZ3%;R$z^b7m*ll2YzHl2FM|U7b!^Z65U#i5*SL_wCBQ$tfWxbRUw=M_*8pGo
zC=Ra!{<TMN*ahssOMvhY{DA`;-T+(__)h`;+@tyPO~5Y__{)I5CUCwS@GBn4)0_tU
z1A&u{WquuggrDFG1^z9-9~CrL0Y6#buL0f`G!y)<czzjh)dByr$oCb1?-pqqfG-p@
zGy(s-C>Oy`K^pp&;71@Yf?I(9kHACl-6$hH-v)dKt`c4YP6^Ko8Ys=PK|ehw{=XUU
z#kk&%-$CFZxP;#ak)B}U|8tNR!NmVR0ZxL~@w)}QBA9sob>Jg-13xN{gdY+(PvO}s
zK|ehw{$C{U6aRk!Wu)iC|EmRl;{ON02YOz?uPE>n{JVl?;{PdupZNb7fs=TCt)Q9U
z9~Ehc|L+iKi2p}K8lv}iMZU!68$`JXc0?M2uMl{M=kF7E2>wMu5ApU*f@XqWEy_i3
zMU?#vU@AYA>m*r|aYeFo7KWz|N;ftJmiAmm|F?eF&o}?`USX2|YamysKGqiA1%Tk&
z@iS4^1XEl1J=8V9)D~`pJP^Ew-<KdW1XEjBM*#_@w(!3N&JDn?!jGO4{C$D*6yVoF
z9_TsA0`(gNm+^Zign{6f<9Afxr?&73$Q3;&`9Ci36Z}VbNYAM)GzETY3x6eWlKhhl
zQyPLvh6$#&Kr&1)wS|I6Lv7)YM7|{ROQKu^KUbt7_*nuE$^XX$9)f=r2ni?2{5u5A
z1XCX_pWi9Uj>iw{KaxKabxC6g!M_Ox5qvw~3GkO-l0WL#2qyVkMja4L^7mEL6Tu{Z
zRn!x~B!Ax*I5z;l7C(AU@c9D&DZoF6XY_m%@D&1o8Swj1PxSodfC~aY$=@HLF6lYR
z+>*dgGDm;(oZx#vGs!i{-`@f!!6b7<(EL02hT!W&xk&zgOXN%PH!aFV^7rQ=Uy`%i
zP%iqG;Nt=h!5M*vWbO}vhtd%Iew2}5lDXdi%@U?DM#4V<x(Gfwzc@E}Vr&d(HM|Ra
z_(0>WuX_8$cP9Vhs{i&)x<2rQU->7xe&n;C{T5yS;roB^ZMx2{R)6FjxPJWjuRnpV
zNB{QXE9m<2PyDk%*SEgqudbx)2O1}?rt1S=c-KL?{`wz%{h9pzJ=fCpW0ya09bJnD
z+yY$}-tgcAU4OOy&;nhb`oOQf`-8Y%`GNoEJ#@Wy@|us)^@1P&{O9QU!pF^jfv*4Y
z-ZwwvL%3e}zNcM9*R%h4aFMRRGJEJ%biLrm@1OZFuD|`=>t^Zt<3IoA7tr;qcm3T-
zx{i(6^ZW|IYsY_pcWX5K#vTiuQxL{TXCjlEupD@yI>tu1f`g~;z=6}ggQqtdwJoEy
zN#S|xI)J(kpsoX`hc^D{(`%A1p+-aJ^x;b~hzsmUD$CkmyS24jo_5O{ORc4vEFR+e
zYVm|suuRKdS(;ohO><>w%Cyoe+h<bi^?E%uw{%p8%JJgE3@231xu$%P_UqGsS4-7l
zt)Xf+z4(BWgN%WC&qQCY>0T7v{@jN5oCmC48P+SqyX^q0SB6*hQG&aCl;F9hjA`X-
z^{mk<seRanA4EE&|E~4Wt!iaVD`TFE)QQ?gsj<|WD^>f`F$b9yXv<h%f~Zfg(Wlou
zZY|Y1TX&16O6BU>s7|kWcx+hJ%9zUaUR%PnB`mb^*zo|Ck&NejpJ2oi7Sey$dpr6<
zTgD=1ACIK+^fKk?jBToV5t~j&>&v-eG?m4c%JrTJy-W$0tj*FHze|=J1|YGDlLz%O
zrMF@`5(A4?##G&?*>^WOl8VwBR`rI}7-!reYtAA4*Da;06{Cw4TKkM?Wy~9-I?AJt
z9-^a%7>2ftY0Fq|c9lU^gS0Z%*J@D2GS*v-X-k;4gsq(}opRYEYIS?RL=Oq+zZ+Ep
zXCLGIWo;R&ZZwii2GZE<%8tjt23U4|!dhypw(eF^t8QtlxZK#L?Z6kUrEqe_7CiQG
zqaFB9Y0KCiJX%}F)zxuzd+>i7$q=Dfrqs$<Yr+_0Rxpyv(#w*1S+X6iZC_f#ppN0s
zJOAR8-c9LNTV4=ky-dkxLmfqXBnB6~OsSVCrL%yTl6Mrsk&N$J8S6B@_rG2~lFHJ`
zSf|R;K4Q(brlyxA^|GW`mJI2?n~q`WxMJbh`C1v%%9vKhMl#RQp)7PLi`}+4>rfVY
zSyFi8Ylr<k=g2Zz<twEBmUqkEXAM8JMGxKTXohxu7P~%+JsM4W=gpYfGNvtKZR1x*
z`O}s$Z5h)J`|1c>qgBSV@-?*RyGeIXhV<W;FE|K;Gdi_pBr9KfnX=RPu9qpvE#Gr#
z5sT>~nelKz)N#dhTrnM240}j)C<||w@MyXl?Xa&M_It4MY2_<=+)+sXJzX<OEn~(F
zdjRNV%2Cb=M$<c52M`uaD`Q$2)5@4u#>zhT$I)~*S{c*Im{!Id<4~p4Y#D2t!#m7E
zD_^5izC!wMC6^sd@2Hir>ABKrS1V&$8Pm#`R>t~@52Ss>N=@}j*C39I+A<ckjA=`l
z+uu=6g9xHlz7hr98eW8bq5u9luEr1XcgAh>8gy9ujA@^-w$E64!pcvWHcZd9u~Dud
z(Agbs!+!+8Mx(Z6v^L2jp<Zj2TeZen?=XO-vE6jnCk(P&&n=xR-H*16Y0H?ljA_eQ
zavf7g*w+#EJ>MDauy3VTw$G&2>-BnSZV8&|qqmy1?N%`b@S<jC<x6pBmXmV$X)i+m
z{WiajRUckwRcd7|?C7E${B@`c9qM8vW*EJUIl5&`y=<8<T>bDYXWCNMELV#stU{-d
zvArp44>#q@b|x%qca2{m{TBu~Z{9JIK|t$QTEEiz)kvC_)~~dFwcBQ9E`$2)Z+-Un
zuAOjXnTBmzh~sKv8FIqhW&LXS`jjl6S(wPACv3J<Fgbs$QaW|YZS=QfI>^B)LmJA0
zrE=Jk=>kp+o0%GIkwW@!eS~}EgGJ1+ndnlh)Plyn=r-%MYI7GHZieLf<u98fgmV9%
zC>bnO#v!+a6Sa*}W2rS)s*YAL;{<UemBqa(XDjX&PCnT^K^#Fp;!K$NeZL1XD1*E}
z`&_^B7s;M*taIiB$*HM6eU|tDTvMAAMR?YpqPQeIgw<aixd}5JHAoo-ABvBhm_IhT
zcw{CVBncDx6ukO#Ia*T<^(2YD@rH^-scBDhqJ+GiBuEM^;5_^;5?5mhi^{2b!(A`0
zwNi8JG}9@ZW}2F<mR2inaT)0@i%GLo+itA6&El=MA;IN=1oL$`R?<*kTv(i)IX+D&
zg4xV(x9Z!i)O4+~y;UvF&Ckr9K$<5nweX#L>TGJN)O0K5D$pOk1}~oKy_nwIuAW{h
z-{lr9(=_qwQt#FADo_fHGn31cczKEUlIR1iqL@T-JG15Tmb*wh*mzlpIrMiP4sk7Q
zxh=QRd|23OWz)c1!opm_JomgBdA)k)zrDgRjDGqJJrS`Lo+f*4Zlxz4BaH7i5-nCw
zJobTMR}K|qmeCWBa@E*_`+fa&bWTk?v@-T^+hXDuWvzVmuY85{-v;#Ge&*W!YRy_1
z)5=({`z^H<xN5A|>i>Ux-vTB_Ri-_GKuCVnC69`_3Wd6YxM{k3rsvW)yH0K^I+Khu
z5m^ypPftyzP2cFQNf<;00us3>xGLfeFo5Bv2%-cpL{M0{i6F52yNb^P>d%j?KJkLP
z?l1rMcAf6&s_Cw-zEpQj=I!UHoG;bY-_$whJ5}%bu3?98n)0V3!5G2V_;LBIv+}5C
z@|9L4Un#CIXvP@t+pAm-LeX$IN*71EI4&3$M;rdlZ}KX|Op7#Qnq{fc<#7RM#;jAu
z8WDcfj9KR>)U)51<QJW0j9|?2h9DRt7+Wx4jNmJ2_i%Dde$fI=m<|6L6-r+)84HuK
zl!781<&l~2oRhIEO#eu&&(!XOL(%R?G}uS~NUP%_{UZy`qj-~)`A1UL=?hF>;9BY_
zN5^;6PfTYFiHbs2(4t*FuT96cl_wM}!h3N(uVT7yNg>Al)8b*nzcXShRD)Q^kLK1F
z*s4F`rkk7Y;;K~jh8=Np3-)pI_ie0-UA<O7GfwHAOjd}SY;Ll-$sQV&2(HcJtAkBs
zJmtoN8;`}Z@n~MwZTNRvwdxmDb{39a4X(j66->~b$iV>IVzh7V0NmzfpR(>EoSnhu
zvMxIvY))AxrXi-O5h~-TONIW07^W;u37_kQ=eqnOpHlvZ(~J0{Gf74Goab|Xu{`JL
znY!3wwrM1rmpQCLXks(A2}gB|`xtHOM1=;BL~wt})M@P-jbt>E)f<2A$s+4iSkN{>
zzkLTtIzLfAo-Xup(%vpD=p{?d^N1Y&FS!Qi&yeFHb<GEbK2i>x2mNJHUxGeUj>sV<
z{UM>($^rhMcOPid9~Syb#q%RVcPpNtzbVl-;B%H7Cn%n03;iF<%=706ojKT~cL;sJ
zK_-2!(9^<ykF=nVmuv8M&lCD!xd!J!KPh~0{-Z))t@xZT^jnv9b=@NEPB|Wu@4v3-
zjdDMrKPJa7<hP(NkmEGr3Hm}g4v@bK`XV_#F2BY7Uo6K_aun&SRbM_X=laz6VEi}9
zZx5CBbUA)4zb(>72!GHR|F5fSF#c<0+;ARa{)8Mw`n__WpfUc>3xCiU^O)iZ`iIg-
zoX7Z|q<CV?pHtVIrM|;8pqD6~XAAu)#S`Oyw2T9;*&)Xr>OL{%M=AcGPg2)l%<oj!
zfc}%>gYkc#y5@YL|5N>Tr_di!eZ-jmjU4z}pf6Rw#rVHo<__mE{<o^WTqN{*^;?Yp
zlXBo1(C<@y!TA4H@sS;u{I9D^e%`eKkW^Z^)vjvi%dc@mm-8v7-TbzhYpVUNYUi8z
zTh06y@^8o?Q_}_d{W1}tu@-(WGFhaLl}Q1OweVea4c5Y_%r(wqEj%qpk)9F)G}gj@
z$y|em{HGL8&_5A*!+ETQNyQUu;RSWgSwdeR2d)AA5+OiiEnKO1Vl8xw4CDL`Iqp&S
z3He{8_=BEQ*FgU7SJ!|(K%NQsE!M)cx&~|EztwMd3VosKBjo?Ja^QN<UsU&SfzSb2
z3pkIpaHr}E*20+jE#&`~a^M=!(?Wn=>>tP+=6{dOIp`0_*xe@M3i<nga&D=NALt&L
zGn|L~-K(yF{JmGc$9c%#^KumFtPCz_$lrl7$04~6GPhlL7U_SNImR{k`)S1!^0!3s
zJWJ?L$$@J?AFg;p{;rpEIDd}NgCaMecL@Ek+~-cAA#;7gANLPBE!W^YWbQG!2IoN^
zD))x-kiVQ<bB7$~3%yjX0lib`Ps_ESA#-n%qey>6eh2yjIoB`p2O9GCBh?qk-}~gZ
zxHrh$Z{;Y`In|d98^=b5hUNV%-;r+rI6QOBj(6P}-8a<T{gx}y?!M>vt>2LLdsnRd
z8QNDZz2{eGpW67c-=kgq{_8HhP1=`#b=@b>-uuq0Za|y*_0MiZ`^$$%_o3Y|F}3uY
z(mwdW!3UvTx#OW_XkW5w_j0u7J@?C(p#9aur#^x9kQ+aD+kMjBf7J`$Li@*KuHJ+8
z#FxH!2io^ep8T7qq<zPsue#yf<M$2y?Qw6o5$!9V{KJjsN&AVr?z|c835OnfE7}W_
zAAAb!<EOv-r{_!i>%YAFNwmGE?)*90JFj^C3ur~`<WtiB&T5h%#v2bmR$d&Gkq)`7
z3}k#jYF$@NTXb})X7R-W1Nn<}6w4NHG{2~YoScx{=c@|AslkDWc1%^JO_c=p{c|;V
z?BSh}UhSA<EN^J>X=EGMEbixS{bx#^`BF|%0<<C}OIN-SI1trDePc;rKfg=jt&9xz
z{ULb0+f+R&f4tL=w2<n+q&AgE>Um*ePigP;bGf{7*8CbjDzjnLxYCjd-|<?J7<on0
z6Q`6WzqTjN;?m?x6y!iO+%Ga4Hicc=t}ixCe({OJ8eC54qI{sqw@wtZt=4cM2?h!;
zK&{G)+%br!MQY*l8S9o*>K@nJl2@I4O8EB-;9tjXwg?<syU7|{PG*>JAr!F@ne=3`
zSc7ZA_d6LvGD0#<V_8&t5e`DKSiEhOHO<<*B}Kq|)ruQ;n8gRl2*Ii;#N2x@S>dHa
z8B`no{owrKjuo|igk)ufy<3#_t9;HHt4`!=<?`Z!WK4@6*KDqag_`aQACg&nW$xVD
zQK`NXY-y3s#nvVIyPC$XYE`9t0Lg4KR<Nz^2bJk<-C|N0CWY}hDU1#Oo~aqJR4Svz
z3~hc!sYcS`6Ot9fPMNpQckd16>0_Qg=IQeWL=%$v-_~q}o1F?lqRXXIDNpMh5rXB7
ze84!}h-!}MK3u0xw&C9oZA(D0$yCbPU`;cGC+`hHGD0#!GEbHki$&JT#_zukY6$Ls
zaC2|WcpeIL+)!!5FN}imz3-TWN6eSD`7sj}?czZ}PE{Ng+ZF}W6YUWneK_ccnsCrx
zBq1|4{QF@`ppzvkh#@p5q_Z=|noM#gl^$F>v<b0;uPTMfjGLvuvN?S+5jRee?_N>*
zu676w*W4t-)HIE2%)>QC2b{&`6K%zwR4q`oK-B_O3l?p=4gY>**3kii4motl@z^29
zQT}=)Yfd3Ofn?e#w1>q5sm!FF3?N0t;HKP+E?+E<2ePqTYLa1UES$MjA5N4^X8zM}
z(kI#>os3pZmF5_r?ad3GKGF1vPA2W?iyQ^9MZxFRO&kklwl-6M4ga2%R`qJVnLJv5
zd~f}smW*1m{ULLJ?X}SdLpsCMDr*3n*%x^7qF`ADr?)(e?2YXWJee#)GN;L+15WER
z1=ybL&1l7J`1fp4nhl2rLcvgY^LV9d<ef-MOGrjYMo5;{e3;D1_9UW~j9M~k$x0S7
zPgKziQ=9i7><)A{yd`K+@+RAZuo*~32&PTPw&~hrVroh<i>Hl}*4=}!G#masr#$;Z
zF(FroDbM3taWI(joGH)sT)j0TTcmg?7sBo+^J_XOUOKKNvrZOMoI6cc$F*eE$zqCg
z`i{|eY%%zb+3@d<K;y3sPj<V^ujvFgX}B!y4VB-HH$uo{PE6)xX2wxs)&a*q`i{|e
zY$mTy`X-+ywPMtYjk&BB(Q#B+zQ65>&J_Z*;ooy5`PrDB&t}qjeSQ)Uuj{o?OBM+S
z(o?xuAf41EY@vwdGuVd0fHl*<c`{kll2J=0k03WFvqgr>@@2)UGiAYot;w(1o_v}i
zEgTGWYoVTKcV9HvH@^V7ILwYz$ZF;F3#~=F@p2jMQD&coUr5H<tL{Q5=mPi9w{~d`
zWf4_X40CNpeb=I;Wy8PcmCqniR(E^>q|CHVnM18VmwPqZB3tjVUp_~bP}WLI9*gy{
zWPV{KvRJ73`z*jI9GMTCia~R$hBvMs8e26#4X7nY<hT(t%HDM@ek3_M<I}P97SZcX
zC6anxfLf_wtc#z^<?E7qN}S}1S_^kSLP}kCS9+F`Z@s*FUWcra0hKc{FjC0mGM<^q
z#r6E)$;csjgyn*bS@Ar=01u9ity_D-noV+rX1!v@kQ|i(m85cH<Jxt@a?M;O%GI&F
zo=l{L{jqP7FAguIs9ZBWlioU>I71%{1%pBP>eZ#Mn3B&>GO9_=+pJkYSzIwEW)ew5
zlg;X6CTqzlWmnu-78!RZqqR|m@5*%Vx4RQr>GE0eJG1TjsO<?(9k8#mCzZn}?oMhy
zM|`lsv`!g!Cw>fGDW4<mPRcXJ)ldSL_6zgekb1RalCgX%ffIKpk>Ef?JI1o%;_igI
zlh!S=-e`}M%}_g>e8B-)vhpu%+le(Wab6B^6Ze+z@A=#GTtWLasF?~6Hx?ov>{hJ3
zq|-2+hU>|6-XdFyO$g~UOs8Qw4Oaufnrr=`ZCUN_q4h>iG|?5!#g3bio`Di_v2R9t
z`dY9V(R!4RNjMtnj|O{7f2}%Bs@{_tdI|JJE8?)adQ*o~75fv6N_>B)kh!L!Pf7J&
z)WALM??pIVIm@F*UFdfZs|F@Q1S=8^72IGPw6N9t(vVQu@bAtRr~fSzvLGC@%FTP_
zy5$!4O7+5V;WsctvQ&c6?(RY_GE=eKxG@q-yX%I()=D9X1-fA~p1SKxLxSefloS?C
zumCmfWm2olRE@HzZGLc-Cc{+bxSN9-DSz7E1*tA9&ypo_9IV>IVu5%jm($|~i}jXR
zA}wF^#fr5_T%=h{r1LU~u<joxKYdv&V9aLq!B{q%oE66;Idya@pGdn%*eI7SOXV{&
z#$Z6m<$1GKV+<B{+%Y~&q-07gr5m#Ux93}Q5+nq6R4I6hIZ(Qky58Y#?aD+X^K1$J
zdddo}A|e5jbyy%(VQ!1UOG=BR@a`r8X>2lD52Q*jy$&M~^Wk68|9(uj`uH>e5{XDE
zrpNLvBI#`mk%-_Bkl2dV6?c2#XgDZE*h}zdp{q#DC0)H%SuKF*kB6?=i^A@8ewd9%
zU1+_EM1q$KYJ~QwNIX)B$aM5Xg9W`?$>Z7G-p^<IZ;42AF!gvOSWYV4#$MXR$>=a3
zX$b%k-@tyTcNV5fL{>(z0}E9pQs_c=$yDR066q)avEknf_E!xF0ZG6(-H2*_(vT34
zXwAIJTdg4>AaPFq*w!6v*<=uqRLHj7?#Un^2?jThuMUQKHqUGeOlGs$fLRJ_&PGFx
z$h@3QCG9gcB1Ybq0K|rWFN{Qk)R6eZWK;GIq=v)???7ru8Z~CN+I}W0gMfs9r2bq4
zWq^cUto{Qe1z%kHj5PX~ZM8;Z5%`SQ@b5)wZBSbnCL8f59*9PI=*8NIcU7waN!es9
zRFh4i3ogA_J8H6N%tTXfIBOND>JMqhq%(#j2lqoR{%}wQ<#b1T`l8(ul%s{sJYw2*
zDV<qXLUSsYNomHkE}ppAOg>>`a<eYnQ``?)4Mo0Vy$nynFXu%%GqTx@0j&KKWrkik
zxU;lJ2wR1ndew@A?{n0w)m%||)1F>=vb5wSArG42iV;BWyH&#y>nMnxOjfA329|Q)
zH~&2iLzUcNq(e6Rd$HuWbeHm><5^;DVZjBNoR7WS&smL#B1d^_Q7(R4mrCw4%Vd@i
zr*oJkOYElH%)&|%YmX~(KR4#Hf{;u$r|t*Me-EMR9>nf@)y!+do3~-X9e2J*@j~uW
z!7G=LED{a|D`sNw-O?f?TL6jrt*>`;Z(#ROuIH&i2-bX}ejEP%xV`F_b<RSBWL7^G
zLNY?KT0Yf|J10W2YV{6nPaZ<DRhzuZYUqs+QcK4Cf6V{K{C_IjPTXa-oqBcv4%v5H
z8Y*r0_mX1TR7vYoC3g=PH=Wv?hRakcqs0tutsd8`Z+aRoE9+eRr}!fzBP2^E?YYey
zw>vak)+`rhdm2_VT+&ckMfc(;e>7B<CqVQkBqIdV-EW(k-MU{Q0Qf#hPjS1nm&$R<
zK_>l4p?`OvN&kz`I}R}EPYL}>vBl$h(3eZrPtc#1<HbWv`ZGd*Qf&4(f0-PAr}%$X
z=-Zc?=Pwufor?eGg#NkW`FWvF!*z0i{=;&U{({iADgIv+`t6GUmxR7a-SZVfuT=c6
z6neY5=c|PN4>`aSbV%LvmxcbK;<-!c0~F7zh2ElgUL*7a>OQX(I;41hMQHRBJV8IF
z?sK=$ql)KsLf@wPe!bAIQ9N%D`Xh=b#{V4oyN61P@qdL}4;tgYTlxYT<A1zd4;o|s
zkc<^*jCo9sBK<A72hg~$cgQ_}#`ynC@x=IN<sNVz<A0#ykMaME^b6-P=5JQ~LEk82
zh4UEyl;V%^?^66R{<`9i@xM>;yi(}n)IDR&Kc#qL%<%zF(C=0}cL{xm8V8L3k%}kA
z|6IlMTA`m+_lYszpm>7brS22se~99FozN-86XXAg>O022Px0IUrwsYOeAPv|{p0X)
z8TY@4EXnkBVJ&=I<_k2|!cigvps^OdA@c<qYvG?nu0Ufg{BMx~&{zv8Ig0e%A_JhY
z7EV<Bu@;_HJh2welu5@mSPL&x{IM3kEHZ}kkpGC{5BhdFiSt+svx+~~!l8;k*1~Cu
zKi0zIiszL=WBuZ{SIKdu;tBc3I>33*F~t*W;T|;(R}1|X#S?4c62<dcq5r7v6Y~FF
z#S`?`)O}(tyj=0TPUu<1^Ln9wuKF(IqW(bs4w5m#y9M(1Ss5eHkUxw&Xvp99WZXeR
z{@x{X2^#YEjEp;I$lrE3b}ITo#S`-PemRG0Ab-DCJRyG{l{vt9$lt3Kf5_jBGOsuf
znfph@A2Npz&V$~e_(T3)Dd*0Y1M>Gl;SU<}_Y2_(`bweK$vuO<O6co_CuqprE9EHC
zTZJcR$lt><4xq0VI-+<&{ywL8UMuvAa-X;cGB>Gsg5D$Yhx5CIK0@)lPUszqC*<${
zNZ)bIhK*w*L&Ij$aMJA`hh=BK=g;RKzi(*r_;>v`+RM^i2kw;i`7;kJv`bI?Mxo8D
z-1Rz~-<8!hw9mfji8r9#v3K%qXs0hd`>)Tvb>Gla8<!vP8)^S|%-c1zS7#4D3hh6v
zxbw|uPa0TxEZWGaXBOJ~uX^_B=jH!j_(A#^v_E^}hks?ZE6?~(`TySg9@~kw|6})G
zfcA-9f4&gy;ZF{K4(;xH-f}D2%*u-k?UN6^>>K$1Tc5hA&?bK!x)=Yy{PMg1740Q=
zy|54M&))bye?=>zD4$aP-%F{Iti!{SDoL{#yj=z6iI$786kDtMBQ_(5o=n!FjHRGT
zk}An&YqC%!8S2&~xeLQtZFUgGPE_>7Z|2Ph-LVb-ezK}(Lmf%AEg_%`Z(V3!V2q_;
zEJYm_C~xpH!&wLA(Hx2gq-tF*u1`+5sB(8)e(S9K=%sP9P97@mwW3TOAsHbV9rfyU
zORZY-$I(BfS#y3x^(7%#bqHp|zyDIm5yS9S<VmtPp?LF_UyK<?s3oJ8j9RkkSbB@C
z)Ro)1$Bdm|>_i56N(sqO(Wx1*;>lZ`hROwGsI=kVPtkDcG<nQ8(tO4dW*Bk3186?H
zwYZC8IIG}=a9?lxl`jluWjJeb6{<?ojzUOC76}Jj=7eE}k?M*~e=0J4m-Nkse?J}H
z8r!1F+p=MI2O2JExMXPreK?UXX3A?uLE(u#jF60wjF8NerA6N{`i@x*$L-(jm@>#x
z2(Em`Z20#xrMlkqB}<g)I$N9c4TW^2f@>+oT)t3DE2^PJ$;fzAYot^YQI_3h#hVu#
zs~zO4TS%G_LMpzf_*#Sos9YO^XnQPsA2W{|^SCKr=aM^EPc$SoTqA;^LhfsT+VJmX
z#jL-CWYuc_G#hc)`UpcpGD5OCln&nP3uud^Ez%M(LB&_`UD}K}l?A7YW;FA?TP1eF
zWwnE%)uUbRU=2aCqSz3EwE=?J@b71}M7lWs%`96&GD0#!GEY>R^w6)ae+~txC8L&X
zTwZF+Qj$2s7^If0C0eo~Bp@Vf10*8^t5O-vi#!%K{Cl~W-eZ2KX~ZB(r?)%;qZ&)F
zLP*8}RIoDBJ<OGR7~R1N0U>tRqE@?uHDI_TB&+3VOGBl_9^sAIi#0$Ob`4M){{39K
zG4Io6)*2xhAsHc=EJS8|odY$~v2D6GnV6c=b9&k+;V&94na3htA$MHE;o3A*(oji5
z<>G~4HvIc}&21J+TX_<eX<suE+gcv>-yP^~SW8Ryei|-mxJ+u!x$>CDf_W^89-T}r
zrnPimKzDw+^IO#C`>8v>!oPP(8+cJZT2Td4Qn{!n3n3XH86g=VS^MonG+Z`h9~vHc
zPcu#~&7|`1X2uA~2+0-$BqIdt**vo?Fqthnp9wat!=1k@^Qa3Iq)q?(#kHfOqs_eH
z7@t{JVIJSTWvEl5PK`P>0=TgIWNa*A8L1u>#du7{V=^9dzS^j{tYt+dZS(P%HvIdg
z=IRN0^4=gMBP1gv^JHl;U4N}0BL8hr>1jq!v+?8hhxGjYy?vz=_%?I1e5D;zHB+b?
zAI*2f(wga`*`tNR(Vp%?!hNmBeQMa&fvj4fsxT1M)V|I`;m+~hG8my~SgH^7Bjjdi
z<N9Q5%N9M?jLNfGAbtmWWoAtnjpb5nSG((y4gX$Yf3`RWwsoM3p+dyMxDhiFne>>R
z&t}qj-CbYpeRF=dIrgo$1s>xTp$sE=b5Egfo*FJ4dnI2wJarmdYW>kj;f+0<nTqAc
zjggoq5tbe3R(GLW>ymm(0<=0Nh~7+DxCc+i)yd36V=K!!4_OB$%j{4y{s7Llw6=Gt
zFB%zGArJ2*ax7ErVevpJGpQ#76M8H)xG6WI%NNVzfvmg_CMOnYS<1r9WMh@mSU9g8
zf-G~;T&F>`yw3=-9O4QIsw%F>PJk+TLds{w@65LAN|)tmnt+bZPaT4+UM(C9$(~i7
zApKE-ET=SZPR7NyBa;3oK^8&Q7M+?bYO)mGZUQ>iugS7&cL=h41F{IJs;6u1z<atJ
zRGGN9gnzH1Cd-<|ftsxHkOMFFF4Sa|dF-1nt;$aHg(pQ@G+EsO<)aZXPKp6>Dv{Ll
z+IC4pR+f%zDwj!V#x$^AHj_^nncQs2lG(gZ7R`lqqUgqy1_M!oteAtAt_dAaCp1|E
zS-u5X1XaETRSN$;EA2Ame=yaK)MQbURp&dnZgU|)R+)nu&4ms?gqkdBvbM&y=nbn=
z>4ViBZ_d%Z16n%zU}<)hK7FvZ#1d%{WkIV)r}ByP;BaUl6bywoj}MIogTc+?tHpHg
zE_sA9v)lB)yX60HDZ^)p_>IL<x}oRt-eiTKLQ4?Ppcd|H`_qN~@6{u=ooefKVxs8p
z-b{s|w$B)`7O~cR7Z|7}8!_E9rrD<}7X96^VVp0A4&gnEQ-Nhd-ID*q)lFp4rvLkT
zmo_LWw5ZUcLd($XPYn9IJK5vuibYqfz=$uBX;E`U&6UwosMsl2EF1p4rl^l}*eEo%
zCl!?=HCfbTC6n_vrR`RZl>5KaHOsfIS#-r>=7xrpri)XP)lxXZ^jMAL9BsR?iJ+=k
zzaAU@y%s6?+H0y|dMvtTSt_u%{V76oA<cy}7q<J!PaiD$VAZ7zVNhYUqBQgEXarR*
zL6r^venoSLJ#Q~ArvE$r-$$6}J!xne$NIOHhaZCq8B{p7YVBxkA6)ui(Fcn@SoFb4
z*Hy1}zw^Pe;osd+jX{R33o?|^Wi%nvV=Yib(kY)T`e4l@>f83PdQQ^WwajN|pN{Ty
zbWh9`hHksXlQ~C=IY({y_c|l0na>IbJ#Zs|tn|q;)eQ~j@S(Yo=0chakt(I-p$Sac
zP+MKxL^51&ri|u6ng?kftaG1C^Pt^4Xv4qP)A5~-?>YDGxb(@QPZsMyN2+hlSfbFP
z=4$!gg_^9QCX1RYBOfqMmtpYPUX7Zo>W{~cCmXfl-y5XjQ>pweqYImjE^K#92{l>N
zWOY8j2CL_=dJdD1wl|KPAj`)fi=e75C*yYRUu^jIrm0*kkWOk7vzif0r7~Kql7~2>
z3tdJRGMdn=?$L6!4^uWUWdl<-w0nC^kVTM1A1wM{WwP$Ao<2`DdXoU)XCy}Jc4@yV
z#|d&2=`Sre>95JT0}nCjn}wb}*rdNM^n*)H`WB(X2b%P)LSLqMenV({z!UVl70=s*
zzGInr{+mJ{sd#=%=yMfM{N~vM%xm@ty+QE=y=#Se{tlrJQ9QpbbV~93j?j-BWM1=K
zq5Bli?+N_{#q&;~mnfc~PZXZuy;qL!sQ!Lm=%W<RyM+Fz;(52w&#C_YK<H7$^M^vC
zZ@72RE7X17BlP)-=e<JzM)eotkNY`PT8uyLr%2;|Kx6#BEx!eg@z>;jKx6#RlluXU
z@&CVaKcF%G8x>ECIqn7LLBB-t#69K|PmKQ)azD5R<NsF06TiDk@x-_staxI~x5|O*
zLGP9E2aWOnJH->@e}UqO@&B!iKd!;}Z&Exl=6h6s_X>Ta9JmIwp?G5apH%(5OXz=4
zJnt5Im*R=>KSc4wn5Ps^(08f+-XrusDxMhss};`;C~YYJmo9gaZvQykAmhG9=251p
z3u|Gw$QWp>g_p=Y7U>BgKw~Z3D>4QeYvJ!@9zkO*Tq=_Y8f)QCA`hUk7EV??A^(sS
zoCp1CAwXj-d|2_sT6kJy4Ck>H-mZ9JE!?DdVlAvxJR$$v<-qlz9}pP>jkOR`Jh2u&
zqj+L1{6+N_YvKKhC)UCbRe$#i{T4aE2lQErC)UC*Re!M--l2HjE%etEPxSBQiYMfM
zR`CS=kT3+#d*pbh;wj{!{y_dlWbE*4hWy<k^9maBcespQkxt6MfrkA3L|V|0zh0RS
z(2&2+i>!c#{B<dwkUw4Vgv>o82d)AA2E`Ndw^PpHJml|rnOD$|zcIxV^7k#7SDc6Z
zy;kvr%$+R<&Vznj<`p#L?|5lJL;kK*JRyGvDV~tO4=A3HzaOjq?iD&B2Yw6s0>$(D
zLjP9v7xK4B@r3;CQT>Jd9ieza=5{Ea_Xv#-c!CBF0KH-3*vQbZyj$cP>GqGqj=jG)
z=(lg(H}u%)S7~T_PyKnJeOqMpQTYF>mv+7VMQM}2juqM~Ui0i2{@;7*wsExobKP4`
zM0?|FFDkUpT=SKa@c)Z1`<s78d&?JZcrV%qA2>9I_RH%wO`ttG^h^Tnz3-fzK|6N)
zp9<}z&)+hO|4&Ulz8mdD$vxMh-SqS+H=upZai6#m?R{6h?k2P+4Lnq6w|?i0(_S;U
zZ|JW--93Z$v+3(Ugf_kM(dXYR?TxQJ>33+4y7}$<%>RG(1+<V!c`E+SY`ebNRF!qc
zBrqm{F$s)GXezG~m5fv}Qprdq<9?)MB&eEms`Z)W*2KLf{QEU6kuKKUX*g8@V-gsX
zux7+sSFD{rJuZBB2(k#W2(k#W2(rqnEUr#wCZgIlJy(cX9Mpz4t{-xnyODvO4D=iq
zG`TF5&&(Ku0U?*?4LzG5JUM=f{738nh0l^~EEh}Zh6ou<uZ@Zu0_R$hqRhOh68^op
z4E!=>8B>;3aQ_~NGB2!C_R1!ZKC@}K_bX!z0%H)|=c(~B2iuyA1j?ivdyzBQK9lV`
z2V5~_nLTA$OH-8D@bA|hFm*f2zY%0P=W{2>nxE#tgCIMCEP^b8tYj>2Xz}S-T9P`@
zJm^<V7ByF`6H%H67mIn&hJSC-%S`tKSp-?g;#0RFAQTO=!jY4Xc)u$n$+D69AT?Rk
zWNDM`Jt_#Y77NHCsOoEPHbD1zU~TyKR%f*%%Scu%8t<ibjXqgrJ2ILJ39<;X2(oA%
zbbe1$b4AS+HCL5V_u5l5DE#{yX_vkzpF}2ISFU1evbI?ZdAHmO&uGFj0L<FltZ)>s
zJMnL0CJcfsf~>k?B)w6C&<Be?SoFd2!Uqe1o;Lk2QV}9jPqO?K+QZ_3RAy371}5}a
zYH(9-Mwc&^#{&|?#{~R!zlZB~B%t%Vr_S$mb|3VpK8e_N`&NhsV%&%svZWr=^Vv)~
zuQwJXL~RzeS=45^(PmM5rO`x46JZ@DLV>gLDdm6oW+JKQwe5ON*CrEFQ_`jlZ7P>Z
zX~wiJPvLAPpD;4H*%d`^`@A-j*C(SIUUnnnZGXRcvE`;;7X7m7()%!N1JgEy@Bmzw
z)Kl`n>=@ej;%QjS*GWIDGBXrG7C}}WAd8?X>{n1_!@u7u7#ADP{7aJ|O@?(rM{nNP
zG#S!lNRy!#CPR5$&^%~FHTq%E4~u?SP2fbom<Mh6_x4C@{a3@$P%zq4Foh0}yr-E=
ziA@+m39|N|dfhCg<J(d?%#G#SEbm2YoxHH<gT<`w%<5iufsZEO2hD>%HV@kH?;bcg
zcpcHj+*r(wWepmHcVD(`sWli;SZ2zlCX1RZYO?kNO%^p*;Xv9&=}-5$v7BwW%!Spq
z7q*8!ST_87ha`Upqbz275lOLw%_xZJlSQ8_`eZRT7Jaas-`mt=wME54A1wM{*)@p-
zRZZGnTCcgX;oons4u*O*&uj}!X0zFV@qxg|_;LBIvvSo-;{jtf%PjA;c9=}rz?2PR
ztJaQo$7?<ReWpNQ%7&UV)&z8<4;Fo}3T1)p2}qf;fhikUIw#jD&6N%RerL$tyW3}P
z0>4HVGMX?p4-GRfmS_4of-Hh8f~=r+j8th}GZNccUU!S;L7E3U1+oaL#J|y>$wqDX
z_q!UB0@uGM4Hg`zeA7k0L>JO0i#}P*ixsSlNLV_@>p+Xvn+s_kv_^R{ny^WWHZ@mF
zHd<H4s1`>P+VJo9v=CD^B%Kv>KC@dhnvl_ij3x{PL*dQiL!-f9aP#<llqQe4WvP5-
z#uy9;xjb*^+5F(i@l)hK$mfO65~=AEOX-H}|JRO=jy9IcoY91gCS)`rqY3R9HVCSG
z3aZR!f2sb(ovBntvrk^Zq}I(0?~&?TwXmP`-;5??Z9>*2Y<CPVHCfdkk2DWfSdT5%
zWKnZf(|@0utNkHmLrYUM*zoV(IvnUsPkyFshzCTml#A<=6NAH{ff|Y^MiUxZCha;)
z0iy}$`A_$w(%8_(^~u<lEqbmQHKkvDvgm_l^IdHs+JYd<$H_;TY_z!mnWCbHwF%1-
zS}p)b_m%NLDl@4k0~2~IHMl;O%<F<fm&XHQf@`zt%7%ZxuaZ8<Y1#`}0h%bwhD3a(
zL14~?nmV5*up`3>>(GYKKDd?kLD6iqR6FNd{^;pmhJfjW<=zR)hJWvBUljt{3u!Ox
zq~|;Bg*CCG&(J<S-RbF0Pxl%IMqg;I`ubN?t7p{L?@(dZbvR+as)E@Q4F{v)uvo;x
z+EgN`=e6y6PS<>+xxzqrUfQWR7~RpH-l(|`_0~SvfnN2%>NzUv;kBz<YVx$<-@A(&
zxDIr&VAvfuVuqxj7}N9FOggVO*4G+3AE&;x%wAX~l#ys;0MqAf1!$*UwM>&$)~nuV
zs34TXnW<QA+!%?a8=I^eAgkTolF5o-veqT_l%6&^CWu~4S+Q@P*$dl0Sv`fx^3VyZ
z&0Xs5jYj%b$Ws|%dCOFLn8jYWDL2zGbD^KigP55r>KcNqGEJ-l-qhTe!?ry639_oC
zv~l!wZ}YPRXqceNIhlbC|Nfu}=;*~8n;@%p9R;6F89`S4*+<*_U?j*Y&kXBmE+oiO
z7VS2RC^cCQ-V_8`UWOBvA$@ADoGnhi62v0#s7rHY!@obQW|Jq#Dr7}2dN;LvPaTO;
zlQl6Y(imEV;e-TP1X)^qI+os|Thq%nte^ABTuAd^i&DB1WVtK~LGz%uIUB5kSg@uo
zCi9@ezyBuft6r2(O`pIT;e~Bh6Ht>yP1d|pPd?kL(I>0grrKxkHku1-KK*^SwCICX
ztIWF3rmU!?TTCFU=CUHF^13)-DpT1#)29Eum#N<;qZ<4_N5&g-XRYzLVOG+HHC(+a
z=0Va?4@AR-bkrU=#1}PO9hfLqhIaPV^4Zerk46eY=&=KQy9JR^hR!x$rh50l5?c0b
zXDSD#a<CUK_wLDW!@oar?+{-mvP|XBpoXj6?>6Sks<{>SZmBi2v<R}Ac7Sj9B4bw4
z`W@igJx2stZb6pkzF7mNS|d0R?dgk#BcgihS-hzn$_67C4YeWFdt2=FmR^OsJxb+J
z)Lhlor5_0Ze%C==UAIenpB&#j(4_Ad`fm>~=?8>9OAcHE`myCE{h-j%LrnT1p|3dD
zq<<{*QpNLOp%aSdPlSF*;+nzd5jnc$DAGF>&qw9l^Ah)r^FNhiT=9HN=sio#^N$OC
zq~iI6(B~<h|0Xm(d*lG!E!VtGzI#&WptQTB1^tNptxKf!!w2KPT>5yZv>5-i^bs`1
z|4|u#&=~W6x&I=4vBZai#`y1(@du6Ze~*kCXpH}NWc)#6{Et#RG5#M_JTd;y$$@J?
zzfJMP_+O)VV*LM?;)(G;P4UF|KOtj_-(vg+6;F&g#vbQEkE*|mF@IFuKj;bdw}kBY
zX9El#au?FIuKkH#y?VSbwlYms`&GE0!~fhXfB$BYF%gL_tc6#|S^#~Y9A}99fW}&Q
zN@NW519GetSt`<(3jrEy;jbdMps^O>iYL~>zsls|Jl4W0#S?4clZq$g{{=a44d`JZ
zKw~X@UGc<PI9&0>TKJISiM8;w3^K0AS~x-Rg#7<d4x9)5KJ|AY|4*s=7m}Y2j{xNF
z4w={UWu75_M~eJ`zE9|LWzIlD{+^Y21r7PzAoEwGcgX~UhWs5O?-kIHzm(z$`FliU
z1?M4ueTpaK?+c12WNwM#3Hn6E6Y}?M`Q1)AAb&>*PtcIR^Mxm9$lvoKf1n|M<H8d(
zWDb9OQvMHmO8zeT3Hf_g{?@ZX`{A=;<Jid1a95WMd)H#4{V@JdUvow9*ui~6KY9HJ
zLuj9S*CAoFm)v!9FWUEg;^rB&kyC%L4ef==E!)vveAyq)2uQnQ@7S4WH$DANA42>7
zU;d)d?$~?tht2PEA3@vyvDv%O{(1bdLi^6Xjd$b!S7#qBv>zM#%zgO(i7$OIe3`VF
zm5+wDzir>p9Vc9J;I-0vJ65Io$M?zBUFDRiuZXt=;)mL1$#uf|ElXuK*SGG0Xm@x&
zNo8i~Rgc-L{UDW@rCZ&>{VJ82rElgwdSScKSvyqlCfHw6nMpuvPc#w){uQ{vRA!yc
zvN0!UR5EI1gFz)@gO*z-;L)-~Sk{RmsA|eRuJVkTxVnUY?=RJFX-$TMxfvAbNRYJ<
zVihb?Mvyfxy3S9r3ItiPt^NrBom)8sSp->YM{9fT_#ptaOlk!U6J&V+vIwd)$wxhz
zaW3aKCx}opwn=kk!@m#o^;gEpUQQuF7D3j?_;LBIvm%WzjR%a`tUl;<K&VH7o>)De
zCgq=dN)TibWD#VA3U<oOHa!<h&MPPB^e&@$FdPX7(o?xuAf41EW;G+0N@cW|p}iv}
zu9S1n8EURv>||<$jW`LS2kK&(GJ+~spvs1SA1sUQY|ES#)6Y?p<wH$YC>RY!qTPKG
zlhDVQ1TkRHTu5^vK~|qPZ)}<eX&%(Kl^3{f3j(nKeKV~PeXtfy5`G*0eMo8#m*t{w
zhbD_4%evhr$ntgNqq)Z=CW_`lnhWbwJ9>j1+hs1Kd9Y~RX*4_rsyRBkv(k~S<?LYJ
z!R2&fCXqDia3Ub6atW$z`1i*Ge+RYUjq4XsbruUEG1dMa=7nwZo|GV~wili6Y){l=
zQIlnVxrq&>2%YS2zTQ5EH(wnk;N~8L1X;P%B+E%kIYCcBRyZ2&i}r}emv~D|C6anx
z+pgzyZ89-6C2iW!rgE8-W=!KYvYC9s$mC{A?qPXtCa-&&Y`+8VYNjmHj;WCT(6^56
zHvIc=%n%Lmn4ZsO(s|vJ3J^|0NuMnGWVOlhotms79HS;n6;`1SmLGkv2(m2RxHJ#a
zJXoK_iJ-~>RN3(FPw4nwrv}`2%11iByK;P|qkAwE-aI}u8Vm+EkFO4fdN$8&3ruFS
z*?{qZzzDJjFH7Y!Gsa**$mMxM&*leDj-Mj`K|U{hmSkhOSV}kaTz-K^6NU(~2(rdj
zF`AIkgjTzaC#D&i2mNCnG~Z;U{AZ823<xF2BFJLOhB|$+2(k#W2(p?`7*Lbtpvj`<
zikd6O+T2ctcW<H+7J#F>4gWqWGE^&18&fs}9ei9IGV!z}nvfuiAd4W2Aj|IgNllh}
z?8s=stqx8G1X*Ra!&WUTf~wAdDjWX&sjC#lG#9#zF06A$>N_uSMia*J)n5e!Sp-=K
zYpXXnJ1FVh+n3>;j_!1HS2=wNvN{8@+<_`77bz(=L-Irk1sP7*rfj1&{QFqjpO)g!
zPn1QJH7`!N-SP5t%JPp>7M-v%>BeG@S)7n{a~$hggov`JyIQEpy*x-aYQw*eJDBgQ
z<?o?amV3(@Q5I2_HNt{kS&gaZsLP@*%Okac2hNbp+Th?&5p1-CpGOnLr0$C+sk;sT
zJ~5og8~K27x)Ig<WsRrBaFf&HJ`|u^7TvNIS=I*XvZ%|VE=!{y7X7g3hgGR=ckMI{
z3jhAQw6AzkKF-LWD9cixk<RanAvYFL7Eu;a7E#u`ViK)-{9Edl&_YNHAuWW-Sl-a$
z)3LOaaFKEvDsw=6nd%)Zgk7cQxlRB3r2HT0J(sC=Wjv6|OzO$NgdR%`u8$@2x_q-d
z9+1?Z3!%c!>eb`(9qHsGx?@89;6SAAYz_nAo>Cbb`_?nm?NH3gZKO=7N2TO1WOE1x
zWp~%tgiE}pnFaAynT7r4_g}nk1K~dD)|<8NJ|%aP<{y+lJ#JX4qPXD_8komrc;r21
zU3E9V56}Dgx}=_xl0wDOEH0SHov%)2Cfr?F$F121NG+A+s+Hl=vw>_*pG?G!z(`^B
ztQK(#tV_oxlKS9!x$bopt{KnF<l?&I*hPxnBP<te%<8$q6bz1zty_D-noV+r_0Mda
zF|sp8U`-}DlS&VcY+So;Sgv`+xFNsOx6B4s$MSkIkrwvHzDd3~y!6GI>6!G_@x&SW
zU`P@H%U7>1eRV=wSSpTdhc*q#m#->)iTjg#Ro%ob&;7AYiIhHuN0-?b7wLkHoZLmd
z%dkJ(Ev2U_CROPVhx(gbhU@hSEV{5HM%EDT+Y{{$m;PGy+Evwi;;5xF`%=ZjtUol+
z<Io#-0eJTQgjCqY(8l%2*p@ANuKYP!y%&x~!A|s|yHHGce!<TL)P+#*iuq4jchk|7
zP|1t0Nt|I9s$jIc2Yo3$>)X(a)-R1<G!iNFA~O}sjT<8|i!;6_g5~_YYHWo|VATM+
zVS95f&?yNQyi)K&ch><&Rqfn^N?u&@S&^21&Vl|nzBOjAn&`>nS0<;>Arp7F>yl$|
z$6S{xbs-$|VwnUZD`ed8eqXBEqf_}rdT=;25UN>S;8?S|K*ejb(}Gv>(}^iVpEPR`
zym9VSpg_h24C=YTlY^7`mcQ2q&;W(3%qw70;^4AkL9Jz7(J|Z{bV88E9#DiJ4noyl
zEV!^Ch=6?)r}sodk!Y}2UV0Kl?ta5e#Ij8`nD(k#HQp$WJFLbY2t+K9T%SA?_EUuN
z${1vQiyoND>8AlK6ciuOe7w0jc9{boMLhB*2lS%;f<i7YQ;0;JwZf5VkyG<GRvzKs
F{|_8V<qrS=

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
zcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Qze5P9QA?;WJ1AX;!D4{N!w?07#J-07lCP
AiU0rr

literal 0
HcmV?d00001

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
GIT binary patch
literal 3864
zcmeHK%Wl&^6b-bM`lP%TJcKlCfJ#-%0u~5V$!#4l%8S@YWmOHa(^@!oWjjSIkoX1`
zd``cKF96P&*shy#Kn)-^T<O{~_ud)b_l!?YPEKmI)$6rdZO)Nz0}q3Xz<a<8z{O4=
z{V?M!e+6YhO?JJchY3Z?@EDUNCD8SHQS4&MU2XvvN0hOJ<W99{A=~<<-IIZgccNf8
z2)X$)D@+9;6Ie7_j@fD%`_?Wfw~a9M!cYopSNNgGcp}YAU$Ijex-xd{v?)W#YlJxv
z%P^Jlw3@)TZh)P}et1wAFxxVkCIqkMs`q^_a78Ew43?&#Mp%MW8Gc*i8%7+<4<(Pg
z(GZzaC_LY3+m>UsTllKj@!;@SM12wX2Zt#KG7EvS12US%8etw%bUgia2e@E$qQp=A
zh#?}8I0FP+c!Wnq4t@k#&p{^e@~a_K8V+8Eewtu@16ci)?*K3f^G(yRtyaB_@lD|5
z6sI)xqh2e@b<ai--U5M2=gO<`fP~_4h*^a#Pe87=Wj6hA#I(qRKGXI3r71r(e{|}*
zS<C6xK_gu~eihr*op!ssYqbo=+&A`28_TvD5lT>uva%PU>itlrULs^7I_t02yW&Al
z#vu4ITobMzdp$-@9OE6ubhs98jgTh*!*v&^B>)-pU!DXHF|KgjKTm>9=ru73Xaw;z
z{(iL?zZL(5A_Um{+zD{WY?w{6<?OfXb=!2X#nZeNQQQ5*OB2?(7kC3yej?s)#2e{`
z=oM`sNu>(^`Klk^QLbUBX~8EG@W1l^XyuQcqf2?<yF0Jp?E83$VdA->AH^a)^iD(m
zfRsJ7c~`tygQ!-`8iBo?z~ui~gr~bIOm1%5Q52M}3d^~P^@i~rs%$vQIJ7f#tmDE$
zfez*!v3H5{n@TeYf7w+1Rxx%+uQ*o2-0!v>BRi=#yHQq|DBl;`hDd#!=~?;aMO$j`
zutAHXMquAA!xu~{+A<GO{XApbraGIsa2+_Y(Ncgo6U+RHikSkEe{~36Q@+cSrD%jN
zs~Vv@#M$WjM-iM^G(|s5eAg?yoH<!JeAFnRgyu_)pq!gsNq$x}|NG)peG#7hhB)MZ
zLQD)T1fZ!(KKqkFQ37i`zrB}f<qNW+^HF`{1cO%^?o@I0Di+@bUpp4LD8CbNtRLNK
zJveIru?ByJH{Kb~Ftt{abNSH;wUUd}zTgKGf1tI`z!}?aX3A)u=9On|JBWJkFyLzb
c01`fpwC53W3AhTp47>uo3am|BNajTN0h6WU?f?J)

literal 0
HcmV?d00001

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
GIT binary patch
literal 15104
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

literal 0
HcmV?d00001

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
GIT binary patch
literal 75712
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
G@_zuRqT?$7

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
zcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5UT+(ClE71`I11I)hQ=GIU6bfQp5xRMx6(6

literal 0
HcmV?d00001

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
GIT binary patch
literal 1242
zcmd5+O-sW-5KV0hwOa6|7KC`vgLe;t2({fpph;^J!CP6Atp?NGl5W5r1b<oof;WGT
z`X(k)ZKUAI2a}zTx5LiPY!-{f!Z0eYhGA4aej5oW8xTSXQXfP-lv$+w0?%e@bi~Yu
zkyD|jIK_7>B@BhH;s7RPX+r9so*x@2`ln6hEcL$Y^N7c76(ti%$<HmRO+rT4X*nKq
zoYtsov0i6nb>MFf#aP5b`oh%8{6;{u;c6}e9tW<rxh!&2;atX_DxlH{w63k9!FtSQ
zjyJNL4!gmQBNmB?K&hb&#3R0D&ggf$F7r$t$9xXrzo;fq31;i7Cl$}K8P2xrg%e?_
zu_>}*#ypIYn1*ouv1?#0p>zzRVD_HiUY$J}-0iD`OPx%vWvFMc_aFz5O>3ZLp$--G
zp1M<d2e1-qHfy<-)9pjwhx|U;#HQURkVcETaqj9kly?_!<l~#BCn6nyo=ZUmXK)u+
zP|@=#UDg(aP{v$i$xTXL6;tKkQ|#JnV<BG(l=POV`5{*BoZ@s-MQ<oiMDdmBTn^^V
r&V6J2a`4YFZXspYwXvi*e!jXx1ge*j@*kkkX1X)Nb5_N#gp~CGunprq

literal 0
HcmV?d00001

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
GIT binary patch
literal 1024
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NSD*F)%PfC@}Xw5P<j~zyZV{ei@L4
ziGjpN!B7eTPz-_qFd6JC0y-QNtHHs(o_Q&$6|h)U0E&T<g9Z=_0C8|;Zb43}e^E+m
lQEG~BVgXDYPzo$B3Zz+q*eNGJIU6R4RS=|O6bxht007HPDf<8b

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Q6|S5QEqtED6M{PC5C>*+_zn06-20LI3~&

literal 0
HcmV?d00001

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
GIT binary patch
literal 923
zcmcIj%}&EG49-?6jSY?*;1n*r0XHsN2dH9Ys72y9W&IfySt4~saO5pG@hW|^mCvP>
z0tHQ~ge5EX7u)%g*hNtkj9KrDG5cODWelSaJ%je3!#gQbRqymafb3V!G~R@8DAbg5
zIHM}Sk3*eBaH+}(bohn*NTl6PNmgC@I0%IlnWN?NT2b+fAkR{@;=B&nyB&L%un&S<
z<f&F%i8bBw5Wb2;76rK{)NjBDpedbr(gjIqZF6;1&PnQkfis&sHw$+P|HxHQ{K|F0
zH;*EVBOXc}K4D`waT`&l`~&fc*3u+?H5*?q{P_cp9r*f%CsM3n@8)XqB)cQmvMjgA
zZrYY!yLC2|r-p4hqWK!a%$NU$8y9Qv1T%x7VN0kL1RfSD(K_!GpgpO7jgqNnA-%VH
KaQ+IYqSz<rO{CTU

literal 0
HcmV?d00001

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
GIT binary patch
literal 2060
zcmeHIOH0E*5MH&`w|Ma=_9A%PN22zsAb1Eu<`)E$gIHq;J$Uxu&&prZZ%QXSm0o(V
zD0JXszn$04>`qAAw(Y$!<_MrkQI|zi(j+VDEhtQH(-<aLrt$^ZnDIUc??15@-ORLJ
z{>=^W3$kDg+6Gnc0z}zET~_b%ssreF3NC>YfSzB+qw29KtEMWmVk~|zP3@;xw}IQy
z+tZ5}7b-d8F9>)4fkJ+E&vQQjmg)Ws{&U@Pl5^k!SZL%ck}nK&<#I>XI-JT?Gxv%I
z#S_I-#jg~<j@+r7PUUgR$15MN@_Uuj-^853Dz0Kftl6nvoQ>ytr4MsF#wJRic04C%
zrJkI;mEM_4ZQxF7%PA<IpkjlS-r>{vqorRJnJ=E=9>DM12X+9Sc$S<JRcE5=OzaSL
jj>#$Q?vr`!J?(rr*IJgpfjxxm0|&q%!2jc8V3qO>+X!kZ

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
zcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5UT+(GY~UD`I11I)hQ=GIU6bfQp5xRMbQUp

literal 0
HcmV?d00001

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
GIT binary patch
literal 9335
zcmeHN-E!MR6n01hX_}<@Ym-1rb_(qjrqeqvnCUR1C~_*WWmQsIa-~u1O(L)*BgrXY
zh8Zq+0d9B<9wCngd`DWP8`+N4KvFYgXVxq4IV<n^+4HwPIXOA0R6dO>mCBUk?X@``
z-b0@dea38iejp$p|AE<?>6a}YHw7w&)Aj|PZlwqoZ-!9|U+GOBqR(VAg@pvY(4r0T
z>NO>bykj+N_c{T+`&ldOfegk3e8$o1nrkV>wuYH|cH%_|^MV%R=;1yK_hKF$d5Iqe
zfX@LQw7jTgCv|V&#5Ay`#Jlffys4XE?2F|&*NB5?6OZsQfE_<$x}P1iX!b?V3zA-E
zC-4)J$r}3LU-=6SKx)%<O|f;O+Q9QV`fzR&Htq3q^tqIA&+H`fgM+UyF)4sK5fiil
zlHdRezQpuk3T`CFT~eZ3=+q`HzK<dQN-v+{-LQE`upLLmKy&FwpDeS@F(%-XwZ=9c
z=NhTZux-{34`^bf)a0Kh7WGLDb7{MfE|w->+E$-yTRSyU#ich?#LYwA>ZNCIY)DzR
zn_k<Cq=U5?hHZw1y%WLH)ta*HT8&rqY=y7ce%mW23@kX){u$5vH(Jn|>Bgq5SuZsU
zKe}vq9scIn^XPH>8K%o~J>U4n_l!3X`$lg21Ji1Jt*H)h@0(Hh9d9PgZ}A}U_x)yS
zuUMR(T_B9b{tJe8*3xRQOPaf-IHd0nE#Br{%vsAzID37_1A6k<9vnr7$E*vp?UPR#
zO$^y!sa>1d@@7`Bt&|4N<<`n|c1;rDYQM>1!rYG*T5f`K!Cn3dns=aqw%RZ?*HG$2
z0CS*&aWm|4mKuV`{OQ3{_R6-LC+uZyQyEuVuv}|Y70VSC!F6;ebs@{SU1))*$nTa8
zVy<2m3uh@Xbx7l`iO{3zcfB_AgCia%{(+Z-5g_kCn?}VROJ@mC8!nNspoVAmP9amt
z&5Ho;5~GokF6K^=Cc{xuhcmB+LE^#Tc)jHCT}fo*_E)m)XYO5b^oHT;TM)o}W~MS9
zna-3H3D)NA60B3fKd$!g-gXp={QaU#Kuj46CP&k7?G`H+%|3+K2fM|B;KZqs)lM@=
z?{3RtLwb2FG`R-w@28A!BeaeF(^j>#7dx7v?z(m5TfA$r?)~U@dL6bO@gI6TXdYig
zfR*2|YCy(J$$5XqObU0QoSzmxK_8jLPu8FR1U$meTPZC+pJBRzmo3));F@6!oFI-q
zI8Vb=&2Z8LbU{T1$D{wLj_E-{6@;8+YZ=SzolRG<EM?a<8@l1>1{HZ$?B1S7r9IXS
zeI&go_W0#2>J(PX?GjvhfsXZz4x>?ZH+9E$P0ey`rEaD{`nu8U>_OtFHSVFP$GUiq
zy-v4ex#tR~Db!0AGinrQtYIjwtx-s_Vk<UNpt?cev6v?;Kp_S5qob0m&o%C51XT4J
z&K&k%Llp(VVNONWZ_^2kkqS1)DPtNOW~jA>`i*<)t3bpei9&6M&F@JNbTUfGp%5MZ
zNBq>Nv}`t$H3H}DR8Mc${5YYrhN$^~r#1AFDmn)Fm`G^sUE9&>E-IFGrnYxp@+igy
zMM~EpXYp~I@J=bIU;XLJYZbbw)pX;u^s;LDUEcNsUMOOYDHXzom^l@1gt2f+$WbPh
zF{NPs)Cy{%`n8f5za#H;R(pm082{ai;^X`lRg0<I$R78O#->tNu-X#WMcsP|{)V$=
z#It%t;+OQB1$%v6<bJy%H_Z8;&~1v$^WNtO081~s&)yLedb(!t#j99Dx3%IvGF?E6
zXimQ3I}4ER%W^=vK)Rli=cW4}dAfM5BE$L$_A;=)8ZJX~tDW6BomQA-ltCq289ul|
znq#EQ8Cr16v4|nV%6c-+ImI&>ZD^@RMmj&!O-lpi(bL3OMlY8Z$}7&xie$WVvkn<?
zU_dV;3=f!PEWynFfK)~*#$!1w75}jKCPyOa=Zcs+Ug5iBl;OTv#*!@YfOkkx;a`TV
z6;@n8C#v|r4E``Pv5Bot+IAfS?Yhsj{D#S?O$yTSK#S@BDrkf}`AhZ}lBpY&%KN{<
zWMI6BK7;33jB^-oV_d+vgmD?;3i=)NyXdRvYv}7Y&@q05@gBze=o@%u7$0E#1mi=D
Ok1$q-Z3v)u(EbJh7Y|<m

literal 0
HcmV?d00001

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
GIT binary patch
literal 30860
zcmeI)U2D@&7zgld;?y}|AP9=!fgx_9B}w;U3&PszaM04ZTnHkR)8x#>rb$WDI(H$0
zUicLRFXo*eLVD?i-$C#*crEDj^kt+_z06|!KkzIkIZ4hrzjK<k*_v**+uhbQ?Gl9*
zN#Bv7V?|}@pP>!fyIY#ZnOR<;^qbO5KdXJc+~5A1)`!~$MOged0z9IeXc}~nsMY5w
zEUUcZJL-@}l|xY6pjf6@q_EsZ(^HkuQK53mvKb$JF271~mZIndc71pfS|48!Kbm(!
zf&c^{009U<00Izz00bZa0SG_<0uX=z1V#mL_ioe!YY>0{1Rwwb2tWV=5P$##AOHaf
zKmY;|NRz;urT4j?h3_A~t?)8UEkj!%FzEur|8BlM=^agR|Jr`t4_^A(xfHi?lAFe_
z0iOR0libM^_igdhug}L%ruTP$sj^+zqw(SI4Im{i0RQC8<2Ca2m%m?w>u+2oxjg%?
zQs6yca^~@x*ngJ-^FOYVT%P@(QDFWjXCAM~$Qim}YT8_UBk~Cf^zXqXSYfKFqR$_<
zr~TgJ@p`bCszFJ(F+U0~rn>)|U%aXAf5Ovkq&j|Xz-gvO#X|Mz!@UYW{{Sg@0e=5^
zNO3Xws!`*KfV+8;ow5wizqb@urmWv-=`Q<!pcr|WOj;U;c0d3E6D`2kp9}Qk+C=v<
zy`5`!`fcNJKspV9>AwHf7oT0yw40Cld3dxwd~zKx7wjMmE&D(@ZBLoHk?sAxv{JO<
z`Ca$uD7YgYgc-3H)KnM+Vmp++ty*?4I3HIt^RNB*bEs+PRdY=r95QUiTy{&Cxiu>a
zLit?fMMs4#H}J)MVPuRAvF!R{t1RoP<hG(#COVArRG_cSi-7i&o+m=paeGJ5A?Ajj
zy*DqK(yr6VMvK-rvN^Hr$yOxn1L;#SoqDh*ovzk)y+}~S-hr>{hN!&~Ua&7i-tGt)
ziHaQw(-bTt^jzM^=1udYs+MTCl#>_pLhSgF3iqY0?)E!C?aTDqMU{xMRpOJ5E9w>W
zo>4ijw9ssNTS21{_~S~gC{(gBuH<jWl=^q(t5#uG*Y&DZ)Q#0@yOSwfJeEihfB*y_
z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_KmvaNI12gh

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM6w3o?HYi&XNV7WS<R@oC1we|}07QoeX#fBK

literal 0
HcmV?d00001

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
GIT binary patch
literal 3428
zcmeHKUvJYe5Kn14`e%Jb8xoQwq&*-ZRUjUaCZwz@Q(#R?k|FUlOq!N#sZ+Tr(jJib
z3<&WF_#FLc!0)nnOPiFIv5A+RboTkqcFuR_J7+eV%`~m?UemO)!#@Nbh&w=n0<hK#
zxGxe({RcSJc(CtHTnyAykTE<%TaPDLo*T+OhP2ClVC|f<u#luzT2xK8-qNMyXRR=p
zjs)$!+YhHW24ji5x^GzRmSc2HFq*oEJRvw6hrWn>F=TyTdT!*0g7FEs!I=n0J`b2L
zSmck$8Vx(*qR(aDjyhcAF)CmZkTJG_RihV9eA*NPI$#2YB?tWsdDc3#+vb}o7twTd
zB>ZTC@fPp_F!P1NF8~Atl}=l?4fACeG^w-P_%xgJdkSpCcHFfi3A4NaqnHRu55l74
z<j$l!0h#0I=V0d_r4*fw$((f{LDBLC=+uzZs9rb&gYv5<&1MrsY(Q$ZZI=gJrd(}^
zh!G^SYP#vPO;bNM_Q9zsUmK+MYA=>CD-BDWwcMJdW2|h_XZ2kO&58kit+)Ci7Bn10
z8=@}q<R=~wW9_iR<QTr8&2b5ZLDnUt=#`?27Cr(ljTdEx0tKwH2gsPt+T=Xo7)vIU
zOuf?veXpl(=s3AcOz<`DqlzhP6T=O7av}06=27Rk*FAiP?`_(i^t}l~_VXmhs~vgm
zk+&)*ZaBJi&~~8QPQ>NVi>@yXCVA*K4d&cA%Az-s1{cyw$uZ8a&|FGkBflS4y|qG8
zl{8EyDD0rg$u0lAa;=|<s0ieo;GYUs*&B!MM{Ha?a$002paP<^*>1sAb#m2l*D?+a
zdY){@9t)*E#Peei%FAmB;p#kc>$G*Q*WWvBFARg!Go#{Idh4}e9>6IN9m(BOdPoie
zK4g!XBc~|i1BM5sDqL&%4wL5KiZu)A_h+&X1t4K2Zb(k4Xzpcv5xOLqe@H12NzzZ2
zG+&kNG%sBg)BMZoqqwm;Ds3qyeQx1?q(x~jDE@c+%;0ZE`wA@Db_X5plCc0?`tn{7
ix~Cv$G5t&ch%9`;B47!034Iy30lW*;mMw(n5AYjPzj|!|

literal 0
HcmV?d00001

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
GIT binary patch
literal 2436
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NW;=GcYg$X(kW{2>t^Bhz|lBKn&uS
z0cn^RNPH9wr4RtcAP4}H!M-A(!$Gkc9PI0vmy%imi&X`n7$*>G0I>iN2WRFM<fQr+
zrKA?6ruZfnz|;Yy!1AI%niYtha`Kb2VUkz{K{`gkXb6mkz-S1JhQMeDjLZ-K0GU-P
AiU0rr

literal 0
HcmV?d00001

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
GIT binary patch
literal 16
PcmZQzU|<jcVi*7b0MGyt

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Q6|G5QEqtED6M{PC5C>*+_zn06^LYNB{r;

literal 0
HcmV?d00001

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
GIT binary patch
literal 1306
zcmcIkOHaZ;5UwH#pk9r5FnjT4ym`_KFAa%6)0P-dWNEjsQg+F98-qWt|H(ff&Xm@a
zhml~MWV*B8Ja%Shx^W!GhEY5-;N#F?z(6<zK1jJ@29%3bQ}_V1oNSsbbW>AUzUdaE
z<HW#Xu2dduG13KaWr?ggqINH*qcrq+O({BW$RL^ujD6o0njoe6a-(Ni1LwtT0nl>B
zjLc*}6@RCiOF{H}#)vN!VbmQHNLjI9shKJ1MXtUkG$fSRT25F<n24q~5a`s_RCrW*
zw(d}|4JZPLgauZC%WYGJ9M4QaSQ6T+dW@Qc{;m9zq<qurHTw+*eD2!9<1SR<!Cfgn
z<YGh=bJ<Kw6%y$apD~Y&B;s6XnZFWn-@R@eqJONrYP%HLnCp98${^SG(!*2jabWeH
zA&-(J--sX3(M}bB(F*;oL7R!L4QQ#`?+ma@aOh<>nhu%5K5QAq5G;0YKvu*4=`R+G
zPl$rEmhr~%cnY?9<u^;FGt;b3;EFQerA&e0@|Tj~0-h>(tBhCRf^BzTLDP+tt^h37
Y1L;mcKxcd^fKWt-L)`yl1gez200`CqL;wH)

literal 0
HcmV?d00001

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
GIT binary patch
literal 1800
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NW-VF)#oT7=!r#fq)Uj00K841H`Wb
zvSDIiiBVz*g#ai9K>(Nx_7wr$%>l&0!M>h(DXA5(SXBUuu>i3K5DNftaAs~nPO5)V
zN@`JRif>{8OdU`PEH4VAS%KIoCqFqGCW%!Lq+>V$Q0P?wJ%S#3AkQlRF)5)Z2~>{~
edaw}0Dgl!OY8tWtl%_^90OhNZ4nR3%AOQfDsXROY

literal 0
HcmV?d00001

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
GIT binary patch
literal 84
rcmZQzU|<LVVi;fqGFX6^6NtHhm>Y<BfS3u2L2}3dq#lGp0LBIYCI|qK

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM6te(nCMa7HNV7WS<R@oC1we|J06=vIMgRZ+

literal 0
HcmV?d00001

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
GIT binary patch
literal 1344
zcmd5+O-sW-5RJ8=u|+{|ddk%w;H?*Hs}w8^*@}27+a#9aHY=M{@$T=+PqV&>8?!b_
zAs{+1yE|`YH!p8LvMkFi%kEleCVVC$3>t8<9eCRld=cwRvk7D`5BgCOASg;P%<T+q
zCok}$K&lWSt#Sfye;^wZDO+ojt(QJ#0T*0dNU>VRw0O5CrEp=oz`49Wznc2vTf~kp
zpWS=G=NRv^l*b`gAxlR*{tf{4#cJ`SAIvqV5*@}V@pN@TYc%c;ro%qMLr=*dN)oBu
zr3@o?K|*dPOq!ThpD<P-;#JNpRf~B3$+<bX^9Cq9C<&)8k93H(v+bbDv|nJmsW7pv
zC`^R^ufh(y=}ooDN2SWiYUcid((Cp5P45|<5Aw=JQ=Z-h^Wsq<8R5n`H{s$anOLjb
yFcUU4DexyeWO&Ie1>a?Cq(4?+cZ!7F6Eb+ffF>80hTgVUFht)#Z~a(^Txsuy&hm@^

literal 0
HcmV?d00001

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
GIT binary patch
literal 1540
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NWy+FfcFzX(kW{2>t^Bhz|lBKn&uS
z0cn^RNPH9wr4RtcAP4}H!M-A(!$Gkc9PI0vmy%imi&X`n7%LEK0I>iN2WRFM<fQr+
srKA?6ruZfnz|;Yy!1AI%8l>JSCqFqGCW%!Lq+=9}hQMeD3}grZ0GUoI0{{R3

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Q6|K5QEqtED7Q}<>V)4BMCAB06<a)L;wH)

literal 0
HcmV?d00001

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
GIT binary patch
literal 5224
zcmeHLUvJ|?5Kr6mnxxHDh?BVE#Azj-&`G?((+SCO?bIT1dbZOOyjX6$ZY&(T_|KIh
zA@RUhKzszg0}uUZz;9gpy0H@yB2J1#SDMUvW@ocAJOAw4+uPel<B8j7H1;j$BH)<p
z0(J`MoCS^_7Mu?}P}}*#2~Q@)QlNL^3(OAl1qPo)F>OmbeGKT_Q7VGO-AL$hvGp@^
z;sj1CM?p3VY4cZFm@*p0g6>d}Oj|XS*S4XkAU;=P{(Z*7$qgHFC&^;YV(#%66w<zH
zrcUTOv1_J7CtO6>2T>dwou3q^LAb7i8)dkQdmV7{T!pcs%iloaBWo(0nt7#0V68rX
z3#){&`z;<8T-r|{*DsBn<LGtfglRS#hklyi`6=KNKwCSD5<m4LG8-wj^A`p%jTYU5
zrmG{&p8=Np1g$3Nmw>Jm$Ii_wGmZW5@+%Pb3TD1N=DVjqV)bvBEl}Zikn1WmL2aft
zS9IHl02=LcCpNeTH^M_nq#Z%Duyk!T3mG|bbD0mZ-AdDtF73HHtorN~RxC9|o5TT4
zLoqGF1Jn4rz$b)zl0i<h*H_5+O>1;U_@FNhB-x_rqe0&ojn8O}JnRAW0f%>{&k#?d
z;md!+iu{(yA?Q-bsuUTI00#i_GE%G@G_N~)FNy*oSX=5q>f6TX4HmbI=$d(fbBXzC
zr!N1d#G41@;)7PQN+(Y!rPYq_Q_5u=W%+GwLmIgY8^&N1((3sB(6%lO6_B1(PR>GS
zMscy);AN0taXvOTIK<boc~?_yd8D7)@~Nchka|H`P*d3?3g7ZLLEd4LDasWKv)KiY
zv0}9v<g(&;4{B6yp;?;VyyBBIb^><ohc0}5i%ox|E0%3)CrVG!DWx3gJY|XR@(a{T
zJa2`aD<Rs_$3t=z@|5R#?h!2VSW;RYt`dekpv=A%$FJ1<V{2qd1Dg!~Mmw=iam-K2
z-_t0C%aGl#eK(ylFN#?@^^?Mj*3zcZ0W^Mv)PnL!rJ-IvDONY?07&p9U}+(v1q69b
zuN9A{c|8&uy^6uwbk}*8ge|_5d6@F}U1#L;B)1fICR0ihbn^_EB85d=Cw17&L5uMN
zoo;2~ChJkPqoXda?FAF)p}B$)9-6E7)?ASft_qA!9vEd3c_A0xf1f_Ypum3rZ3Jv_
za#_cCM~bQ{vZb9Xc8|K!&lJx?+4kRZ7Dj%;g-nd%@?gp3`MHDFlhI&oX(Ou0pHnU4
zOKnM2?SE-ZyfFU>^N?$nP>V<K@{csNPa7mbgWk!y)o%tOQ+&@7>GQ8OB4@$7gUGJ+
z<|4D~ctu*ZqZD~!=O)q@v_v9%t^vpy^z)AunhZ6c9@m$vW`^}8=ObY~Iee$1FLjMS
z0DGo61j=u@+N}^{zZXQ4?=YdQ9|JIUeg>W37rdgQdpQ7Z0k?rWz#ZTQ_`85TKoihd
Kwi#Iea{LV_4cRjQ

literal 0
HcmV?d00001

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
GIT binary patch
literal 6408
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NW;AGcYg#X%Gg1|3JV9VKQ()`E@`V
zCI*!nrG`uhfMOH`fXQHA70}h7SPc&L^~_62tpLR;2rB?F9}sH*u>cSUXXX~<r1}@7
zq!y*7_$C%0$39425{OxW*eNGJIU8mOkdIvgSvN@0C>RZa(GVC7fzc2kJ_JTfZ{ovh
zROM(0jE2By2n?PO0JW6|5)?=br0s*kZl)IrD;)=gO9sFZ4I@1MCya3aFBsweAAl@3
nYUOAMpoBoMldho&P-LLn0w|6e<<Knz2E!<5%XRh<+XeyvduT&@

literal 0
HcmV?d00001

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
GIT binary patch
literal 6320
zcmeI$%PT}-7zXeU6AGCrAq^=OZpFeaNrYI8a$C&8WyYoCItxXN63Qq^xin!hxeFP?
zViprqENDi}Lawu*Tnj0aD8+n_TUP#n^Pa`|owIotU(Y$;a}HyS=>wgoufxS@I<7k<
z_oWLNjEQbVWn7I}3ca=QaIi@R{c@_HHbf1j&qGkRw+O>$w&1+_6vh-kL4CAWIiDNv
z76y$cDKOPg2+e)9FiY75caq1TOl*L*>s?s9c@B?8Utvk3M8W5tWk<rQ;0$=<ki&Yb
z3O-J0U~{_;z7!gvI?4u_>kaHVeuti`0ZKl%UlR+(iX1qcR1O1uTj0di01Vxjh0`M&
zFrwiAE@eHyIH`LDpSS)L43jMh&@`C`)7z@yc0mWsjU0hiwhZ%+w&DJY9m;zh&|c<U
z$>%B(!{LQbDy+FIf_J7ms2c8uj=FK!nz;twf-Fv5esaKA&*2;*K?-jjC<l~({@haz
zC<lLYK>dLF0g58^0|ECV+81bF_`5IAdI<SD>Gyp={eb!b^#i^F^gf^*P!7KTD+~L1
apd2v9I|5^A{&weob$<PM&vW7N@BIfgyfJeC

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
xcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM6!QUTR**OlOM=)=Ir+)iKn6?!D*!@t2U!3B

literal 0
HcmV?d00001

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
GIT binary patch
literal 2016
zcmd^A&x_MQ6i&_7?e4nZQH8?bNu(@z@U{s3xfI(??X-9-lVn<g>5NX26|W-V!J~gq
z{}TTe^?PZG(`Kd8li-7wy!qbuChyJ5%&b<cmE#<LcO0iW5q>OT+<}r*pr@l)M5^%I
zeFx+uA9Q3AV6dUW%;h<rXE|Xk18qVKX_iOO(>1aaNZx~jj*F=u@<7DGv~`><6-|B_
z>I`)-RuJ5Fa{#k`t)oOTkR#uSG}4Nta`{%m&J0z3&Qqa6VM3k`g}M!>0kq{C2DcRJ
z0WKFyh2DFW36*BcsfyAB;|I_>^rS!1NklneK*Y*V2;L+#ehNKr8Y4bj^3+6X@dAKb
zIP(CqUlssfxsap0JT0wb27!|Zwcen4H5+@c$sX(XY#xgR?ET9D(v}?5MoJ;iqnzkY
zACCrJvjg(|ffnI^+Pii0fA`L7@!8Ppbg?XtJwK7=2LA?I>QFYAyjWZ`nAhvg_^8{S
zjHg4e_ILFREL#rmDrTYXmK(eC2fE{K4?H$8nPhWqm|#ZErI9L-Oy^bQ`xTu1>4UT-
zho}E{x%2)NzE`($PjqJf!NmJ2rCraEEmc<I)7Ff)o7b?P@XTjPAQV&C(wC-z^^dXS
zH<G2=c7VIT168nWIlKcn1Ai1VQOcyH&y$}nq2iP$4hcc_$!6o5gT%&a)=skV;@{kY
z)-AUodoefoXI!kfS)Gqimvh>DlvJsJX8^0MSO;%lP%vF=5Hc%A2!gtwpkJV00sbj~
Hg39s><ehwe

literal 0
HcmV?d00001

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
GIT binary patch
literal 1548
zcmdnDZ{I#Q1_lORAPz1`%u7iuN(nCUP0V8k3NY}nFfcFzX(kW{2>t^Bhz|lBKn&uS
z0cn^RNPH9wr4RtcAP4}H!M-A(!$Gkc9PI0vmy%imi&X`n7&{Pa0I>iN2WRFM<fQr+
srKA?6ruZfnz|;Yy!1AI%niYtha`Kb2VUkz{K{`gkXb6mkz+eag03f_74FCWD

literal 0
HcmV?d00001

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 00000000000..a1a976ed494
--- /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
GIT binary patch
literal 325
wcmZQz7zMx(u-yj)K-v<BS&A$3l7TcM5Q6|a5QEqtED6M{PC5C>*+_zn06>-pMgRZ+

literal 0
HcmV?d00001

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
GIT binary patch
literal 8122
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
aKc^uHms<{(ra^f{EKNq2{a+1D5almAf(Tv!

literal 0
HcmV?d00001

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
GIT binary patch
literal 66048
zcmeI)O-|cT7zI$%(n9G!9d!p?2Lq%IA_iVTRA*2XQWP;j463fod+3VbT@tIP3dPu#
zs!HdRu1xIb7dQ#S>$Yv%<EpA&m(&;YWiwwj^~G$yd|FOa)z7MWQVvPxy39^FCi|P$
zKQ^z+!Th%Z`9w20cAs|l^ZKjs`&wS_W^tLjYjW<{?b+?;pFePIugCR#>oVKkH~VLg
zta(wA>9g)&y|nk*mjl+n8hB6sFEf4rj{I(UuiW>hBs28hx9i(S-T!W)J>E~a&*8*-
zec0^hK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB=E90_lf$9r5mI59#>dO}hPmCSCt+Ne}<Nq@Vw{q`Uu6
z((Uh)^!ocIz4^x^9s9>7o%|t5kAG;=;UAOq_s1sP{V9^({!~e4f6A2Y3;_ZJ2oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjX@30!ZcEpt_NO8365XP=kN;<9@#Knmb_F2JXfbiwPdXXmGjlU1`=Et=W<
zJnOp2^^YZaF2K?0_b=bNO@7^Xp`14a2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
P0RjXF5FkL{9}C<7&8s@>

literal 0
HcmV?d00001

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
GIT binary patch
literal 33362
zcmeI*O-oc!9Dwm_U`U`yZITEB7aF#xMW7Z`Qo<}oFmd6cLTnb%czv77@}()?Mtj)^
zu5{PBzQKk12z`?13+Qu*Lq~|*Dm-U+xPQ)E=Fa`i>~B#N#my|s+NqRJ&Q+!=d2OOP
zS}RZF&Fp1aJ<FOn#KP7AD;!;?USQ|Q9n|med;h0DC-*-4a<%u;FV0KWKR0=Qx3$sL
z=>L5$9-H_l9rybCvEg4ARtv@XVlww6^R;BYp3Hs8d^?%%GIvo}XXzZZQX8G83ly%^
zL7jAkx+z>UeEtm@pj-5ShG>Ky(qno;RhptjTA~m1k@jexzS1}PPCw{Ah4XWorWu;0
zIhv;$y`Y!$iWcZKy`gxGW#$!nOLclj?`f6RXq`4_lNz)|+Z4asC+5!-y~Dl5xtz@5
zUgQ2)GLI+oqhziyyA)7B0R<FLKmi35P(T3%6i`3`1r$&~0R<FLKmi35P(T3%6i`3`
z1r$&~0R<FLKmi35P(T3%6i`3`1r$&~0R<FLKmi35P(T3%6i`3`1r$&~0R<FLKmi35
zP(T3%6i`3`1r$&~0R<FLKmi35P(T3%6i`3`1r$&~0R<FLKmi35P(T3%6i`3`1r$&~
z0R<FLKmi35P(XnbD^Re8`F1T-8ZDRdr<H~9Q1{SfYNo;J^yKrfafZVBNgZ_L&G;zz
EL-1%zo&W#<

literal 0
HcmV?d00001

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
GIT binary patch
literal 16
PcmZQ%U|<jcVi*7b0N?-;

literal 0
HcmV?d00001

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
GIT binary patch
literal 276
zcmdnDZ{I!!21Wq}1_ptU%$(E|=lq<~+`Qlt-^4ti7$XoX0x_p&Kz?y%NoGDwkO?Tr
z%)r0`q!=y(F&9`fOdCj+0YqE^VvrdeAPiO#8Ws*@g8)dL5hMx1K#%|;V0;50n=L3c
tIlm|cDk=q}xga7gsl~}a9@rL;EJzcGqX@*fTncsyJ_TT}01aSZ006D)B{Tp4

literal 0
HcmV?d00001

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
GIT binary patch
literal 24576
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

literal 0
HcmV?d00001

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
GIT binary patch
literal 289
zcmdnDZ{I!!21Z#11_ptU%$(E|=lq<~+`Qlt-^4ti7$Xp?0x_p&Kz?y%NoGDwkQpe*
z0#VQaq!{i1F&EeXm{ul`Fi6=YAO@Mk3Bq6{E{P?HKn@6i6flBhK^W);FbU%u0C{Xd
ssmb|8DL@uTR0@c>AR;cQ#mPV(*cy;5NE&1sA?JeKLr75r&>#o^0K`8gga7~l

literal 0
HcmV?d00001

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
GIT binary patch
literal 1048576
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
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
I@V^)MHw%PAtN;K2

literal 0
HcmV?d00001

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
GIT binary patch
literal 289
zcmdnDZ{I!!21Z#11_ptU%$(E|=lq<~+`Qlt-^4ti7$Xp?0x_p&Kz?y%NoGDwkQpe*
z0#*Pb85r&WF&EeXm{ul`Fi7$e5QEGC0Zy<Ix17Xu2%iCD4kJhwq5`Z5%m(odfS4^P
qH95a11*$>{NOM6%TvCgZfjo!~s39QJ2sjtw9s-IWegYcCzyJV7P$hN%

literal 0
HcmV?d00001

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
GIT binary patch
literal 131072
zcmeIu!41G53<R*-p!;vx4QL4gsT=fRgy0g$$4SP>om{owFpJ84RJoenIoEpXi8J*H
z`^#)v1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
I2>e^%0nt7Q_W%F@

literal 0
HcmV?d00001

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
GIT binary patch
literal 292
zcmdnDZ{I!!21W%21_ptU%$(E|=lq<~+`Qlt-^4ti7$XoX0x_p&Kz?y%NoGDwkO?Tr
z!N9-*q!=CpF&9`fOdCj+ffa}^0Wru7kO0Jdx6+)PpwwcwoWyjX7zi)}F(XJ32m?U_
zh=B18fNZv))a3l46sV{akmiDjxTF>*19@QEK(ZiBAk$QV7{kRN380I?CWD+sNRa?g
H4FmuH+*2sm

literal 0
HcmV?d00001

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
GIT binary patch
literal 32768
zcmeIzF%Ezr3<XfR|Dkn2Mj}C=9Ucn_(BI}Ql&h{XxZfYEm($96_w9W2-R%3nb*t)E
z=j_VcoqH<(bf2hx(S5V@&o)Qad9uy*enU0_1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
L5FkK+K$yS-N)@0V

literal 0
HcmV?d00001

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
GIT binary patch
literal 299
zcmdnDZ{I!!21a!T1_ptU%$(E|=lq<~+`Qlt-^4ti7$Xp?0x_p&Kz?y%NoGDwkQpe*
z0#VQaq!^w7F&EeXm{ul`FdGnG0%DLk>_99GRuZ0?nVwM+T#%YvQdF7?6b1p1iHsn1
zAk4tP0VZL510atrC^b31C<Vv@iAn)67evG*wKy5b16v4^1xbTUBjj+f%LplI02%}V
E0HV<<+yDRo

literal 0
HcmV?d00001

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
GIT binary patch
literal 524288
zcmeI!Jq~~%3<l7i&2xEpCI>r^5JmZUCJrRnetkF)aURk2jfYQbzTB?$`@H|{dSBQ7
zyWeR2N6#}`zS;AxmVfnmcAIbadCgzHpY^_<WdGULeCq#CK2KhEuk2w>fB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk
z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs
z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U
zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N
g0t5&UAV7cs0RjXF5FkK+009C72oNAZfWReC0K@n+F8}}l

literal 0
HcmV?d00001

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 00000000000..105af65c138
--- /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
GIT binary patch
literal 349
zcmZQz7zMx(2;Bz+KspeJS&A$3l7TcM5MKgf2_R;IVnZO!>Xeh8oDF1v6tF{S5E}yn
E06Oyse*gdg

literal 0
HcmV?d00001

diff --git a/CEP/Calibration/pystationresponse/test/tStationBeamNCP.py b/CEP/Calibration/pystationresponse/test/tStationBeamNCP.py
new file mode 100644
index 00000000000..bed4602c339
--- /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 00000000000..28e06978533
--- /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 00000000000..f69e21ff7d1
--- /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 00000000000..f967776d2c3
--- /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 2320c39a326..a7b83510990 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)
-- 
GitLab