package com.elphel.imagej.cameras;
import java.util.Properties;

import org.apache.commons.configuration.XMLConfiguration;

import com.elphel.imagej.calibration.CalibrationFileManagement;
import com.elphel.imagej.calibration.DistortionCalibrationData;
import com.elphel.imagej.calibration.Distortions;
import com.elphel.imagej.calibration.EyesisAberrations;
import com.elphel.imagej.calibration.LensDistortionParameters;
import com.elphel.imagej.common.GenericJTabbedDialog;
import com.elphel.imagej.common.WindowTools;

import Jama.Matrix;
/*
 **
 ** EyesisCameraParameters.java
 **
 ** Copyright (C) 2011-2014 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  EyesisCameraParameters.java 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.
 **
 **  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */
import ij.gui.GenericDialog;


    public  class EyesisCameraParameters{
    	final public String [] distortionModelDescriptions= {
    		"Radial model",
    		"Non radial with shift/elongation, non cummulative",
    		"Non radial with shift/elongation, cummulative",
    		"With non-radial polynomial terms"
    	};
    	final int [] distortionModels={0,100,101,200};
    	public int defaultLensDistortionModel=200;
    	public double [] goniometerHorizontal; // goniometer rotation around "horizontal" axis (tilting from the target - positive)
    	public double [] goniometerAxial; // goniometer rotation around Eyesis axis (clockwise in plan - positive
    	public EyesisSubCameraParameters [][] eyesisSubCameras=null;
    	public double [] interAxisDistance; // distance in mm between two goniometer axes
    	public double [] interAxisAngle;    // angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
    	                            // clockwise when eyesis is in 'normal' position, looking to the target
    	public double [] horAxisErrPhi;   // angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
    	public double [] horAxisErrPsi;   // angle in degrees "horizontal" goniometer axis is rotated around moving X axis (up)
    	public double [] entrancePupilForward; // common to all lenses - distance from the sensor to the lens entrance pupil
    	public double [] centerAboveHorizontal; // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each SFE)
    	public double [][] GXYZ=null; // [numStations]{x,y,z}  coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system

    	// non-adjustable parameters, not parts of vector
    	public int     numStations;
    	public double [] stationWeight; // reprojection error weights (close station - relax errors)
    	private boolean is_tripod= false; // when true - make goniometerHorizontal rotation around "vertical" axis and "goniometerAxial" - around
    	private boolean cartesian=false; //
        // rotated horizontal.
    	private int defaultSensorWidth=      2592;
    	private int defaultSensorHeight=     1936;
    	private int defaultDecimateMasks=       1;

    	public int    shrinkGridForMask=4; //2; //shrink detected grids by one point for/vert this number of times before calculating masks
    	public double maskBlurSigma=    -3; //2.0;   // blur sensor masks (>0 - pixels, <0 - in grid units)
    	public double badNodeThreshold=0.1; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	public int    maxBadNeighb=      1; // maximal number of bad nodes around the corrected one to fix
    	public int    minimalValidNodes=10; // do not use images with less than this number of non-zero nodes (after all applicable weight masks)
    	public int    weightMultiImageMode=1; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set to weightMultiExponent power)
    	public double weightMultiExponent= 1.0;
    	public double weightDiameterExponent=1.0; // if( >0) use grid diameter to scale weights of this image
    	public double weightYtoX=1.0; // relative Y-to-X errors weight (to somewhat compensate for rectabular shape of the sensor)
    	public double minimalGridContrast=0.4; // (normally max ~0.8)
    	public double shrinkBlurSigma = 4.0;
    	public double shrinkBlurLevel = 0.5;
    	public double balanceChannelWeightsMode=-1.0; // <0 - use defaults, 0 - keep, >0 balance by number of points to this power
    	public double removeOverRMS=2.0;            // error is multiplied by weight function before comparison (more permissive on the borders
    	public double removeOverRMSNonweighted=4.0; // error is not multiplied (no more permissions on tyhe borders
    	public boolean invertUnmarkedLwirGrid = true; // LWIR grid currently is saved shifted (not compensating color inversion)
        public int [] extrinsicIndices={6,7,8,9,10,11,12,13,14,15,16}; //support variations
        public double [][] variationsDefaults={
        		null,               // 0
        		null,               // 1
        		null,               // 2
        		null,               // 3
        		null,               // 4
        		null,               // 5
        		{10.0,0.1,0.0,1.0}, // 6 goniometerHorizontal
        		{10.0,0.1,0.0,1.0}, // 7 goniometerAxial
        		{10.0,2.0,0.0,1.0}, // 8 interAxisDistance
        		{10.0,0.2,0.0,1.0}, // 9 interAxisAngle
        		{10.0,0.2,0.0,1.0}, // 10 horAxisErrPhi
        		{10.0,0.2,0.0,1.0}, // 11 horAxisErrPsi
        		{10.0,2.0,0.0,1.0}, // 12 entrancePupilForward
        		{10.0,2.0,0.0,1.0}, // 13 centerAboveHorizontal
        		{10.0,2.0,0.0,1.0}, // 14 GXYZ0
        		{10.0,2.0,0.0,1.0}, // 15 GXYZ1
        		{10.0,2.0,0.0,1.0}  // 16 GXYZ2
        };

        public boolean isTripod() {
        	return is_tripod;
        }

        public boolean isCartesian() {
        	return cartesian;
        }

        public boolean isLwirUnmarkedInverted() {
        	return invertUnmarkedLwirGrid;
        }

        public int    tiltIndex=6;
        private ParameterVariationCosts []  parameterVariationCosts=null;

        public boolean isExtrinsic (int index){
        	for (int i=0;i<this.extrinsicIndices.length; i++) if (this.extrinsicIndices[i]==index) return true;
        	return false;
        }
        public boolean isTilt (int index){
        	return (index==this.tiltIndex);
        }
    	public boolean isVarianceCostSet(int index){
    		return (parameterVariationCosts!=null) && (index< parameterVariationCosts.length) && (parameterVariationCosts[index]!=null);
    	}
    	public double varianceCostScale(int index){
    		if (!isVarianceCostSet(index)) return -1.0;
    		return parameterVariationCosts[index].scale;
    	}
    	public double varianceCostVariationAbs(int index){
    		if (!isVarianceCostSet(index)) return -1.0;
    		return parameterVariationCosts[index].variationAbs;
    	}
    	public double varianceCostVariationDiff(int index){
    		if (!isVarianceCostSet(index)) return -1.0;
    		return parameterVariationCosts[index].variationDiff;
    	}
    	public double varianceCostVariationExponent(int index){
    		if (!isVarianceCostSet(index)) return -1.0;
    		return parameterVariationCosts[index].exponent;
    	}
    	public String varianceCostVariationName(int index){
    		if (!isVarianceCostSet(index)) return null;
    		return parameterVariationCosts[index].parName;
    	}
    	public int getVarParsLength(){
    		return parameterVariationCosts.length;
    	}
    	public boolean editCostProperties(
    			int index,
       			String parameterName,
       			String parameterDescription,
       			String parameterUnits) {
    		if (!isExtrinsic(index)) return false; // does not have varaince parameters
    		getVariationCosts();
    		if (parameterVariationCosts[index]==null) parameterVariationCosts[index]=new ParameterVariationCosts(
					this.variationsDefaults[index][0],
					this.variationsDefaults[index][1],
					this.variationsDefaults[index][2],
					this.variationsDefaults[index][3]);
    		return parameterVariationCosts[index].showVarianceDialog(
           			parameterName,
           			parameterDescription,
           			parameterUnits);
    	}
        private ParameterVariationCosts [] getVariationCosts(){
        	if (this.parameterVariationCosts==null){
            	int max=0;
            	for (int i=0;i<this.extrinsicIndices.length; i++) if (this.extrinsicIndices[i]>max) max=this.extrinsicIndices[i];
        		this.parameterVariationCosts=new ParameterVariationCosts [max+1];
        		for (int i=0;i<this.parameterVariationCosts.length;i++) this.parameterVariationCosts[i]=null;
        	}
        	return this.parameterVariationCosts;
        }
        public void getCostsPropertiesXML(String prefix,XMLConfiguration hConfig){
    		getVariationCosts();
    		for (int i=0;i<this.extrinsicIndices.length;i++){
    			int index=this.extrinsicIndices[i];
//    			System.out.println("getCostsPropertiesXML("+prefix+",hconfig) index="+index);
            		if (hConfig.configurationsAt(prefix+"varianceCosts_"+index).size()!=0){
//    				System.out.println("hConfig.configurationAt(prefix+\"varianceCosts_\"+index).isEmpty()=false");
    				this.parameterVariationCosts[index]=new ParameterVariationCosts(
    						this.variationsDefaults[index][0],
    						this.variationsDefaults[index][1],
    						this.variationsDefaults[index][2],
    						this.variationsDefaults[index][3]);
    				boolean isSet= this.parameterVariationCosts[index].getPropertiesXML(prefix+"varianceCosts_"+index+".", hConfig);
    				if (!isSet) this.parameterVariationCosts[index]=null;
    			}
    		}
    	}
        public void setCostsPropertiesXML(String prefix,XMLConfiguration hConfig){
    		if (this.parameterVariationCosts==null) return;
    		for (int i=0;i<this.extrinsicIndices.length;i++){
    			int index=this.extrinsicIndices[i];
    			if (this.parameterVariationCosts[index]!=null) {
    				hConfig.addProperty(prefix+"varianceCosts_"+index,"");
    				this.parameterVariationCosts[index].setPropertiesXML(prefix+"varianceCosts_"+index+".", hConfig);
    			}
    		}
    	}
        private class ParameterVariationCosts{
    		public double scale = 1.0; // 1 pixel
    		public double variationAbs  =0.0; // variation of the parameter to cost 1 pixel
    		public double variationDiff =0.0; // variation of the parameter to cost 1 pixel
    		public double exponent=         1.0; // 1.0 - square diff
    		public String parName=null;
 //   		public ParameterVariationCosts(){}
    		public ParameterVariationCosts(
    				double scale,
    				double variationAbs,
    				double variationDiff,
    				double exponent,
    				String parName){
				this.scale=scale;
				this.variationAbs=variationAbs;
				this.variationDiff=variationDiff;
				this.exponent=exponent;
				this.parName=parName;
    		}
    		public ParameterVariationCosts(
    				double scale,
    				double variationAbs,
    				double variationDiff,
    				double exponent){
				this.scale=scale;
				this.variationAbs=variationAbs;
				this.variationDiff=variationDiff;
				this.exponent=exponent;
    		}
    		@Override
			public ParameterVariationCosts clone(){
    			return new ParameterVariationCosts(
    					this.scale,
    					this.variationAbs,
    					this.variationDiff,
    					this.exponent,
    					this.parName);
    		}

        	public void setPropertiesXML(String prefix,XMLConfiguration hConfig){
        		hConfig.addProperty(prefix+"scale",this.scale+"");
        		hConfig.addProperty(prefix+"variationAbs",this.variationAbs+"");
        		hConfig.addProperty(prefix+"variationDiff",this.variationDiff+"");
        		hConfig.addProperty(prefix+"exponent",this.exponent+"");
        		if (this.parName!=null) hConfig.addProperty(prefix+"parName",this.parName+"");
        	}
        	public boolean getPropertiesXML(String prefix,XMLConfiguration hConfig){
//    			System.out.println("getPropertiesXML("+prefix+",hconfig)");
        		boolean isSet=false;

        		if (hConfig.getString(prefix+"scale")!=null){
//        			System.out.println("getPropertiesXML("+prefix+",hconfig), hConfig.getString(prefix+\"scale\")!=null");
        			this.scale=Double.parseDouble(hConfig.getString(prefix+"scale"));
        			isSet=true;
        		}
        		if (hConfig.getString(prefix+"variationAbs")!=null){
//        			System.out.println("getPropertiesXML("+prefix+",hconfig), hConfig.getString(prefix+\"variationAbs\")!=null");
        			this.variationAbs=Double.parseDouble(hConfig.getString(prefix+"variationAbs"));
        			isSet=true;
        		}
        		if (hConfig.getString(prefix+"variationDiff")!=null){
//       			System.out.println("getPropertiesXML("+prefix+",hconfig), hConfig.getString(prefix+\"variationDiff\")!=null");
        			this.variationDiff=Double.parseDouble(hConfig.getString(prefix+"variationDiff"));
        			isSet=true;
        		}
        		if (hConfig.getString(prefix+"exponent")!=null){
//        			System.out.println("getPropertiesXML("+prefix+",hconfig), hConfig.getString(prefix+\"exponent\")!=null");
        			this.exponent=Double.parseDouble(hConfig.getString(prefix+"exponent"));
        			isSet=true;
        		}
        		if (hConfig.getString(prefix+"parName")!=null){
//        			System.out.println("getPropertiesXML("+prefix+",hconfig), hConfig.getString(prefix+\"parName\")!=null");
        			this.parName=hConfig.getString(prefix+"parName");
        			isSet=true;
        		}
        		return isSet;
        	}

           	public boolean showVarianceDialog(
           			String parameterName,
           			String parameterDescription,
           			String parameterUnits) {
           		String title="Setup costs for image set variance of "+parameterName;
        		GenericDialog gd = new GenericDialog(title);
        		gd.addMessage("Parameter: "+parameterName+" - "+parameterDescription);
				gd.addNumericField("Effective cost of the image set when the "+parameterName+" variance reaches value below",this.scale, 2,4,"pix");
				gd.addNumericField("Variance amount from the average of "+parameterName+" to result in the specified cost", this.variationAbs, 3,8,parameterUnits);
				gd.addNumericField("Variance amount from the p to result in the specified cost", this.variationDiff, 3,8,parameterUnits);
				gd.addNumericField("Exponent of the cost vs. variance (1.0 - squared err "+parameterName+" to result in the specified cost", this.exponent, 3,8,"");
        	    WindowTools.addScrollBars(gd);
        		gd.showDialog();
        		if (gd.wasCanceled()) return false;
				this.scale=         gd.getNextNumber();
				this.variationAbs=  gd.getNextNumber();
				this.variationDiff= gd.getNextNumber();
				this.exponent= gd.getNextNumber();
				this.parName=parameterName;
				return true;
           	}
    	}

    	public int getNumStations() {return this.numStations;}
    	public int getNumChannels() {
    		return getNumChannels(0);
    	}
    	public int getNumChannels(int numStation) {
    		return this.eyesisSubCameras[numStation].length;
    	}
//    	this.eyesisSubCameras[numStation].length
    	public EyesisCameraParameters () {} // just create new instance, all parameters data will be provided additionally
    	public EyesisCameraParameters (
    			int numStations,
    			boolean isTripod,
    			boolean cartesian,
    	    	double goniometerHorizontal, // goniometer rotation around "horizontal" axis (tilting from the target - positive)
    	    	double goniometerAxial, // goniometer rotation around Eyesis axis (clockwise in plan - positive
    			int numSubCameras,
    	    	double interAxisDistance, // distance in mm between two goniometer axes, positive if the vertical axis (when Eyesis is head up) is closer to the target
    	    	double interAxisAngle,    // angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
    	    	                            // clockwise when eyesis is in 'normal' position, looking to the target
    	    	double horAxisErrPhi,   // angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
    	    	double horAxisErrPsi,   // angle in degrees "horizontal" goniometer axis is rotated around moving Z axis (CW looking at target)
    	    	double entrancePupilForward, // common to all lenses - distance from the sensor to the lens entrance pupil
    	    	double centerAboveHorizontal, // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    	    	double GXYZ_0, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    	    	double GXYZ_1, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    	    	double GXYZ_2, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    	    	int sensorWidth,
    	    	int sensorHeight,
    	    	int    shrinkGridForMask, //shrink detected grids by one point for/vert this number of times before calculating masks
    	    	double maskBlurSigma,      // blur sensor masks (in grid units)
    	    	int    decimateMasks,       // reduce masks resolution
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int minimalValidNodes,
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    	    	double  weightMultiExponent,
    	    	double  weightDiameterExponent, // if( >0) use grid diameter to scale weights of this image
    	    	double weightYtoX,
    	    	double minimalGridContrast,
    	    	double shrinkBlurSigma,
    	    	double shrinkBlurLevel,
    	    	double balanceChannelWeightsMode,
    	    	double removeOverRMS,
    	    	double removeOverRMSNonweighted,
    	    	boolean invertUnmarkedLwirGrid
    			){
    		double [] GXYZ={GXYZ_0,GXYZ_1,GXYZ_2};
    		setSameEyesisCameraParameters (
    				numStations,
    				isTripod,
    				cartesian,
    				goniometerHorizontal, // goniometer rotation around "horizontal" axis (tilting from the target - positive)
    				goniometerAxial, // goniometer rotation around Eyesis axis (clockwise in plan - positive
    				numSubCameras,
    				interAxisDistance, // distance in mm between two goniometer axes
    				interAxisAngle,    // angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
    				// clockwise when eyesis is in 'normal' position, looking to the target
    				horAxisErrPhi,   // angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
    				horAxisErrPsi,   // angle in degrees "horizontal" goniometer axis is rotated moving Z axis (CW looking at target)
    				entrancePupilForward, // common to all lenses - distance from the sensor to the lens entrance pupil
    				centerAboveHorizontal, // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    				GXYZ, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    				sensorWidth,
    				sensorHeight,
    				shrinkGridForMask, //shrink detected grids by one point for/vert this number of times before calculating masks
    				maskBlurSigma,      // blur sensor masks (in grid units)
    				decimateMasks,       // reduce masks resolution
    				badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    				maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    				minimalValidNodes,
    				weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
        	    	weightMultiExponent,
        	    	weightDiameterExponent, // if( >0) use grid diameter to scale weights of this image
        	    	weightYtoX,
        	    	minimalGridContrast,
        	    	shrinkBlurSigma,
    		    	shrinkBlurLevel,
    		    	balanceChannelWeightsMode,
        	    	removeOverRMS,
        	    	removeOverRMSNonweighted,
        	    	invertUnmarkedLwirGrid
    		);
    	}
    	public EyesisCameraParameters (
    			int numStations,
    			boolean isTripod,
    			boolean cartesiam,
    	    	double goniometerHorizontal, // goniometer rotation around "horizontal" axis (tilting from the target - positive)
    	    	double goniometerAxial, // goniometer rotation around Eyesis axis (clockwise in plan - positive
    			int numSubCameras,
    	    	double interAxisDistance, // distance in mm between two goniometer axes
    	    	double interAxisAngle,    // angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
    	    	                            // clockwise when eyesis is in 'normal' position, looking to the target
    	    	double horAxisErrPhi,   // angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
    	    	double horAxisErrPsi,   // angle in degrees "horizontal" goniometer axis is rotated moving Z axis (CW looking at target)
    	    	double entrancePupilForward, // common to all lenses - distance from the sensor to the lens entrance pupil
    	    	double centerAboveHorizontal, // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    	    	double [] GXYZ, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    	    	int sensorWidth,
    	    	int sensorHeight,
    	    	int    shrinkGridForMask, //shrink detected grids by one point for/vert this number of times before calculating masks
    	    	double maskBlurSigma,      // blur sensor masks (in grid units)
    	    	int    decimateMasks,       // reduce masks resolution
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    minimalValidNodes,
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    	    	double  weightMultiExponent,
    	    	double  weightDiameterExponent, // if( >0) use grid diameter to scale weights of this image
    	    	double weightYtoX,
    	    	double minimalGridContrast,
    	    	double shrinkBlurSigma,
    	    	double shrinkBlurLevel,
    	    	double balanceChannelWeightsMode,
    	    	double removeOverRMS,
    	    	double removeOverRMSNonweighted,
    	    	boolean invertUnmarkedLwirGrid
    			){
    		setSameEyesisCameraParameters (
    				numStations,
    				isTripod,
    				cartesian,
    				goniometerHorizontal, // goniometer rotation around "horizontal" axis (tilting from the target - positive)
    				goniometerAxial, // goniometer rotation around Eyesis axis (clockwise in plan - positive
    				numSubCameras,
    				interAxisDistance, // distance in mm between two goniometer axes
    				interAxisAngle,    // angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
    				// clockwise when eyesis is in 'normal' position, looking to the target
    				horAxisErrPhi,   // angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
    				horAxisErrPsi,   // angle in degrees "horizontal" goniometer axis is rotated moving Z axis (CW looking at target)
    				entrancePupilForward, // common to all lenses - distance from the sensor to the lens entrance pupil
    				centerAboveHorizontal, // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    				GXYZ, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    				sensorWidth,
    				sensorHeight,
    				shrinkGridForMask, //shrink detected grids by one point for/vert this number of times before calculating masks
    				maskBlurSigma,      // blur sensor masks (in grid units)
    				decimateMasks,       // reduce masks resolution
    				badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    				maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    				minimalValidNodes,
    				weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
        	    	weightMultiExponent,
        	    	weightDiameterExponent, // if( >0) use grid diameter to scale weights of this image
        	    	weightYtoX,
        	    	minimalGridContrast,
        	    	shrinkBlurSigma,
    		    	shrinkBlurLevel,
    		    	balanceChannelWeightsMode,
        	    	removeOverRMS,
        	    	removeOverRMSNonweighted,
        	    	invertUnmarkedLwirGrid
    		);
    	}
    	void setSameEyesisCameraParameters (
    			int numStations,
    			boolean isTripod,
    			boolean cartesian,
    	    	double goniometerHorizontal, // goniometer rotation around "horizontal" axis (tilting from the target - positive)
    	    	double goniometerAxial, // goniometer rotation around Eyesis axis (clockwise in plan - positive
    			int numSubCameras,
    	    	double interAxisDistance, // distance in mm between two goniometer axes
    	    	double interAxisAngle,    // angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
    	    	                            // clockwise when eyesis is in 'normal' position, looking to the target
    	    	double horAxisErrPhi,   // angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
    	    	double horAxisErrPsi,   // angle in degrees "horizontal" goniometer axis is rotated moving Z axis (CW looking at target)
    	    	double entrancePupilForward, // common to all lenses - distance from the sensor to the lens entrance pupil
    	    	double centerAboveHorizontal, // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    	    	double [] GXYZ, // coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
    	    	int defaultSensorWidth,
    	    	int defaultSensorHeight,
    	    	int    shrinkGridForMask, //shrink detected grids by one point for/vert this number of times before calculating masks
    	    	double maskBlurSigma,      // blur sensor masks (in grid units)
    	    	int    defaultDecimateMasks,       // reduce masks resolution
    	    	double badNodeThreshold, // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    	    	int    maxBadNeighb, // maximal number of bad nodes around the corrected one to fix
    	    	int    minimalValidNodes,
    	    	int    weightMultiImageMode, // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    	    	double  weightMultiExponent,
    	    	double  weightDiameterExponent, // if( >0) use grid diameter to scale weights of this image
    	    	double weightYtoX,
    	    	double minimalGridContrast,
    	    	double shrinkBlurSigma,
    	    	double shrinkBlurLevel,
    	    	double balanceChannelWeightsMode,
    	    	double removeOverRMS,
    	    	double removeOverRMSNonweighted,
    	    	boolean invertUnmarkedLwirGrid
    			){
    		this.numStations=numStations;
    		this.is_tripod=isTripod;
    		this.cartesian = cartesian; // Need to set each subcamera?
	    	this.defaultSensorWidth=defaultSensorWidth;
	    	this.defaultSensorHeight=defaultSensorHeight;
	    	this.shrinkGridForMask=shrinkGridForMask; //shrink detected grids by one point for/vert this number of times before calculating masks
	    	this.maskBlurSigma=maskBlurSigma;      // blur sensor masks (in grid units)
	    	this.defaultDecimateMasks=defaultDecimateMasks;
	    	this.badNodeThreshold=badNodeThreshold; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
	    	this.maxBadNeighb=maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
	    	this.minimalValidNodes=minimalValidNodes;
	    	this.weightMultiImageMode=weightMultiImageMode; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
	    	this.weightMultiExponent=weightMultiExponent;
	    	this.weightDiameterExponent=weightDiameterExponent; // if( >0) use grid diameter to scale weights of this image
	    	this.weightYtoX=weightYtoX;
	    	this.minimalGridContrast=minimalGridContrast;
	    	this.goniometerHorizontal=new double[numStations];
    		this.goniometerAxial=new double[numStations];
	    	this.interAxisDistance=new double[numStations];
	    	this.interAxisAngle=new double[numStations];
	    	this.horAxisErrPhi=new double[numStations];
	    	this.horAxisErrPsi=new double[numStations];
	    	this.entrancePupilForward=new double[numStations]; // common to all lenses - distance from the sensor to the lens entrance pupil
	    	this.centerAboveHorizontal=new double[numStations]; // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
	    	this.GXYZ=new double[numStations][];
	    	if (numSubCameras>0) this.eyesisSubCameras=new EyesisSubCameraParameters[numStations][];
	    	this.stationWeight=new double[numStations];
	    	for (int numStation=0;numStation<numStations;numStation++) {
	    		this.stationWeight[numStation]=1.0;
	    		this.goniometerHorizontal[numStation]=goniometerHorizontal;
	    		this.goniometerAxial[numStation]=goniometerAxial;
		    	this.interAxisDistance[numStation]=interAxisDistance;
		    	this.interAxisAngle[numStation]=interAxisAngle;
		    	this.horAxisErrPhi[numStation]=horAxisErrPhi;
		    	this.horAxisErrPsi[numStation]=horAxisErrPsi;
		    	this.entrancePupilForward[numStation]=entrancePupilForward; // common to all lenses - distance from the sensor to the lens entrance pupil
		    	this.centerAboveHorizontal[numStation]=centerAboveHorizontal; // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
		    	this.GXYZ[numStation]=new double [3];
		    	for (int i=0;i<3;i++) this.GXYZ[numStation][i]=GXYZ[i];
		    	if (numSubCameras>0) initSubCameras(numStation,numSubCameras);
	    	}
    	}

    	@Override
		public EyesisCameraParameters clone() {
    		EyesisCameraParameters result= new EyesisCameraParameters ();
    		copyData(
        			this.numStations,
        			this,
        			result);
    		return result;
    	}


    	/**
    	 * Copy parameters from source EysesisCamerParameters to destination, trimming/expanding number of stations
    	 * @param newNumStations new number of stations
    	 * @param source source EysesisCamerParameters
    	 * @param destination destination EysesisCamerParameters
    	 */
    	public void copyData(
    			int newNumStations,
    			EyesisCameraParameters source,
    			EyesisCameraParameters destination) {
    		destination.numStations=newNumStations;
    		destination.is_tripod=source.is_tripod;
    		destination.defaultSensorWidth=source.defaultSensorWidth;
    		destination.defaultSensorHeight=source.defaultSensorHeight;
    		destination.shrinkGridForMask=source.shrinkGridForMask; //shrink detected grids by one point for/vert this number of times before calculating masks
    		destination.maskBlurSigma=source.maskBlurSigma;      // blur sensor masks (in grid units)
    		destination.defaultDecimateMasks=source.defaultDecimateMasks;
    		destination.badNodeThreshold=source.badNodeThreshold; // filter out grid nodes with difference from quadratically predicted from 8 neighbors in pixels
    		destination.maxBadNeighb=source.maxBadNeighb; // maximal number of bad nodes around the corrected one to fix
    		destination.minimalValidNodes=source.minimalValidNodes;
    		destination.weightMultiImageMode=source.weightMultiImageMode; // increase weight for multi-image sets (0 - do not increase, 1 - multiply by number of images in a set)
    		destination.weightMultiExponent= source.weightMultiExponent; // if( >0) use grid diameter to scale weights of this image
    		destination.weightDiameterExponent=source.weightDiameterExponent;
    		destination.weightYtoX=source.weightYtoX;
	    	destination.minimalGridContrast=source.minimalGridContrast;
	    	destination.shrinkBlurSigma=source.shrinkBlurSigma;
	    	destination.shrinkBlurLevel=source.shrinkBlurLevel;
	    	destination.balanceChannelWeightsMode=source.balanceChannelWeightsMode;
	    	destination.removeOverRMSNonweighted=source.removeOverRMSNonweighted;
	    	destination.invertUnmarkedLwirGrid=source.invertUnmarkedLwirGrid;
    		destination.goniometerHorizontal=new double[destination.numStations];
    		destination.goniometerAxial=new double[destination.numStations];
    		destination.interAxisDistance=new double[destination.numStations];
    		destination.interAxisAngle=new double[destination.numStations];
    		destination.horAxisErrPhi=new double[destination.numStations];
    		destination.horAxisErrPsi=new double[destination.numStations];
    		destination.entrancePupilForward=new double[destination.numStations]; // common to all lenses - distance from the sensor to the lens entrance pupil
    		destination.centerAboveHorizontal=new double[destination.numStations]; // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    		destination.GXYZ=new double[destination.numStations][];
    		if (source.eyesisSubCameras!=null) destination.eyesisSubCameras=new EyesisSubCameraParameters[destination.numStations][];
    		else destination.eyesisSubCameras=null;
    		destination.stationWeight=new double[destination.numStations];
    		for (int numStation=0;numStation<destination.numStations;numStation++) {
    			int srcNumStation=(numStation<source.numStations)?numStation:(source.numStations-1); // 0-1 !
    			if (srcNumStation < 0) break; // source does not have any stations?
    			destination.stationWeight[numStation]=source.stationWeight[srcNumStation];
    			destination.goniometerHorizontal[numStation]=source.goniometerHorizontal[srcNumStation];
    			destination.goniometerAxial[numStation]=source.goniometerAxial[srcNumStation];
    			destination.interAxisDistance[numStation]=source.interAxisDistance[srcNumStation];
    			destination.interAxisAngle[numStation]=source.interAxisAngle[srcNumStation];
    			destination.horAxisErrPhi[numStation]=source.horAxisErrPhi[srcNumStation];
    			destination.horAxisErrPsi[numStation]=source.horAxisErrPsi[srcNumStation];
    			destination.entrancePupilForward[numStation]=source.entrancePupilForward[srcNumStation]; // common to all lenses - distance from the sensor to the lens entrance pupil
    			destination.centerAboveHorizontal[numStation]=source.centerAboveHorizontal[srcNumStation]; // camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each
    			destination.GXYZ[numStation]=new double [3];
    			for (int i=0;i<3;i++) destination.GXYZ[numStation][i]=source.GXYZ[srcNumStation][i];
    			if (destination.eyesisSubCameras!=null){
    				destination.eyesisSubCameras[numStation]=new EyesisSubCameraParameters[source.eyesisSubCameras[srcNumStation].length];
    				for (int i=0; i<destination.eyesisSubCameras[numStation].length;i++) if (source.eyesisSubCameras[srcNumStation][i]!=null){
    					destination.eyesisSubCameras[numStation][i]=source.eyesisSubCameras[srcNumStation][i].clone();
    				}
    			}
    		}
    		destination.cartesian = source.cartesian;
    	}

    	public void setProperties(String prefix,Properties properties){
    		properties.setProperty(prefix+"isTripod",this.is_tripod+"");
    		properties.setProperty(prefix+"cartesian",this.cartesian+"");
    		properties.setProperty(prefix+"defaultSensorWidth",this.defaultSensorWidth+"");
    		properties.setProperty(prefix+"defaultSensorHeight",this.defaultSensorHeight+"");
    		properties.setProperty(prefix+"shrinkGridForMask",this.shrinkGridForMask+"");
    		properties.setProperty(prefix+"maskBlurSigma",this.maskBlurSigma+"");
    		properties.setProperty(prefix+"defaultDecimateMasks",this.defaultDecimateMasks+"");
    		properties.setProperty(prefix+"badNodeThreshold",this.badNodeThreshold+"");
    		properties.setProperty(prefix+"maxBadNeighb",this.maxBadNeighb+"");
    		properties.setProperty(prefix+"minimalValidNodes",this.minimalValidNodes+"");
    		properties.setProperty(prefix+"weightMultiImageMode",this.weightMultiImageMode+"");
    		properties.setProperty(prefix+"weightMultiExponent",this.weightMultiExponent+"");
    		properties.setProperty(prefix+"weightDiameterExponent",this.weightDiameterExponent+"");
    		properties.setProperty(prefix+"weightYtoX",this.weightYtoX+"");
    		properties.setProperty(prefix+"minimalGridContrast",this.minimalGridContrast+"");
    		properties.setProperty(prefix+"shrinkBlurSigma",this.shrinkBlurSigma+"");
    		properties.setProperty(prefix+"shrinkBlurLevel",this.shrinkBlurLevel+"");
    		properties.setProperty(prefix+"balanceChannelWeightsMode",this.balanceChannelWeightsMode+"");
    		properties.setProperty(prefix+"removeOverRMS",this.removeOverRMS+"");
    		properties.setProperty(prefix+"removeOverRMSNonweighted",this.removeOverRMSNonweighted+"");
    		properties.setProperty(prefix+"invertUnmarkedLwirGrid",this.invertUnmarkedLwirGrid+"");
			properties.setProperty(prefix+"numSubCameras",this.eyesisSubCameras[0].length+"");
    		properties.setProperty(prefix+"numStations",this.numStations+"");
    		for (int numStation=0;numStation<this.numStations;numStation++){
    			properties.setProperty(prefix+"stationWeight_"+numStation,this.stationWeight[numStation]+"");
    			properties.setProperty(prefix+"goniometerHorizontal_"+numStation,this.goniometerHorizontal[numStation]+"");
    			properties.setProperty(prefix+"goniometerAxial_"+numStation,this.goniometerAxial[numStation]+"");
    			properties.setProperty(prefix+"interAxisDistance_"+numStation,this.interAxisDistance[numStation]+"");
    			properties.setProperty(prefix+"interAxisAngle_"+numStation,this.interAxisAngle[numStation]+"");
    			properties.setProperty(prefix+"horAxisErrPhi_"+numStation,this.horAxisErrPhi[numStation]+"");
    			properties.setProperty(prefix+"horAxisErrPsi_"+numStation,this.horAxisErrPsi[numStation]+"");
    			properties.setProperty(prefix+"entrancePupilForward_"+numStation,this.entrancePupilForward[numStation]+"");
    			properties.setProperty(prefix+"centerAboveHorizontal_"+numStation,this.centerAboveHorizontal[numStation]+"");
    			properties.setProperty(prefix+"GXYZ_0_"+numStation,this.GXYZ[numStation][0]+"");
    			properties.setProperty(prefix+"GXYZ_1_"+numStation,this.GXYZ[numStation][1]+"");
    			properties.setProperty(prefix+"GXYZ_2_"+numStation,this.GXYZ[numStation][2]+"");
    			for (int i=0;i<this.eyesisSubCameras[numStation].length;i++) {
    				this.eyesisSubCameras[numStation][i].setProperties(prefix+numStation+"_subCamera_"+i+'.',properties);
    			}
    		}
 //   		setCostsProperties(prefix,properties);
    	}
    	public void getProperties(String prefix,Properties properties){
    		if (properties.getProperty(prefix+"isTripod")!=null)
    			this.is_tripod=Boolean.parseBoolean(properties.getProperty(prefix+"isTripod"));
    		if (properties.getProperty(prefix+"cartesian")!=null)
    			this.cartesian=Boolean.parseBoolean(properties.getProperty(prefix+"cartesian"));
    		// For old compatibility
    		if (properties.getProperty(prefix+"decimateMasks")!=null)
    			this.defaultDecimateMasks=Integer.parseInt(properties.getProperty(prefix+"decimateMasks"));
    		if (properties.getProperty(prefix+"sensorWidth")!=null)
    			this.defaultSensorWidth=Integer.parseInt(properties.getProperty(prefix+"sensorWidth"));
    		if (properties.getProperty(prefix+"sensorHeight")!=null)
    			this.defaultSensorHeight=Integer.parseInt(properties.getProperty(prefix+"sensorHeight"));
    		// New version
    		if (properties.getProperty(prefix+"defaultDecimateMasks")!=null)
    			this.defaultDecimateMasks=Integer.parseInt(properties.getProperty(prefix+"defaultDecimateMasks"));
    		if (properties.getProperty(prefix+"defaultSensorWidth")!=null)
    			this.defaultSensorWidth=Integer.parseInt(properties.getProperty(prefix+"defaultSensorWidth"));
    		if (properties.getProperty(prefix+"defaultSensorHeight")!=null)
    			this.defaultSensorHeight=Integer.parseInt(properties.getProperty(prefix+"defaultSensorHeight"));

    		if (properties.getProperty(prefix+"shrinkGridForMask")!=null)
    			this.shrinkGridForMask=Integer.parseInt(properties.getProperty(prefix+"shrinkGridForMask"));
    		if (properties.getProperty(prefix+"maskBlurSigma")!=null)
    			this.maskBlurSigma=Double.parseDouble(properties.getProperty(prefix+"maskBlurSigma"));

    		if (properties.getProperty(prefix+"badNodeThreshold")!=null)
    			this.badNodeThreshold=Double.parseDouble(properties.getProperty(prefix+"badNodeThreshold"));
    		if (properties.getProperty(prefix+"maxBadNeighb")!=null)
    			this.maxBadNeighb=Integer.parseInt(properties.getProperty(prefix+"maxBadNeighb"));
    		if (properties.getProperty(prefix+"minimalValidNodes")!=null)
    			this.minimalValidNodes=Integer.parseInt(properties.getProperty(prefix+"minimalValidNodes"));
    		if (properties.getProperty(prefix+"weightMultiImageMode")!=null)
    			this.weightMultiImageMode=Integer.parseInt(properties.getProperty(prefix+"weightMultiImageMode"));
    		if (properties.getProperty(prefix+"weightMultiExponent")!=null)
    			this.weightMultiExponent=Double.parseDouble(properties.getProperty(prefix+"weightMultiExponent"));
    		if (properties.getProperty(prefix+"weightDiameterExponent")!=null)
    			this.weightDiameterExponent=Double.parseDouble(properties.getProperty(prefix+"weightDiameterExponent"));
    		if (properties.getProperty(prefix+"weightYtoX")!=null)
    			this.weightYtoX=Double.parseDouble(properties.getProperty(prefix+"weightYtoX"));
    		if (properties.getProperty(prefix+"minimalGridContrast")!=null)
    			this.minimalGridContrast=Double.parseDouble(properties.getProperty(prefix+"minimalGridContrast"));
    		if (properties.getProperty(prefix+"shrinkBlurSigma")!=null)
    			this.shrinkBlurSigma=Double.parseDouble(properties.getProperty(prefix+"shrinkBlurSigma"));
    		if (properties.getProperty(prefix+"shrinkBlurLevel")!=null)
    			this.shrinkBlurLevel=Double.parseDouble(properties.getProperty(prefix+"shrinkBlurLevel"));
    		if (properties.getProperty(prefix+"balanceChannelWeightsMode")!=null)
    			this.balanceChannelWeightsMode=Double.parseDouble(properties.getProperty(prefix+"balanceChannelWeightsMode"));
    		if (properties.getProperty(prefix+"removeOverRMS")!=null)
    			this.removeOverRMS=Double.parseDouble(properties.getProperty(prefix+"removeOverRMS"));
    		if (properties.getProperty(prefix+"removeOverRMSNonweighted")!=null)
    			this.removeOverRMSNonweighted=Double.parseDouble(properties.getProperty(prefix+"removeOverRMSNonweighted"));
    		if (properties.getProperty(prefix+"invertUnmarkedLwirGrid")!=null)
    			this.invertUnmarkedLwirGrid=Boolean.parseBoolean(properties.getProperty(prefix+"invertUnmarkedLwirGrid"));

    		boolean multiStation=true; // new default
    		int newNumStations=1;
    		int numSubCameras=0;
    		if (properties.getProperty(prefix+"numSubCameras")!=null) numSubCameras=Integer.parseInt(properties.getProperty(prefix+"numSubCameras"));
    		if (properties.getProperty(prefix+"numStations")!=null){
    			newNumStations=Integer.parseInt(properties.getProperty(prefix+"numStations"));
    		}else {
    			multiStation=false; // old config format
    		}
// TODO: trim/expand stations
    		updateNumstations (newNumStations);
//    		this.numStations

// read old/new format data
//    		this.numStations=newNumStations;
    		if (multiStation){
    			for (int numStation=0;numStation<this.numStations;numStation++){
        			if (properties.getProperty(prefix+"stationWeight_"+numStation)!=null)
        				this.stationWeight[numStation]=Double.parseDouble(properties.getProperty(prefix+"stationWeight_"+numStation));
        			if (properties.getProperty(prefix+"goniometerHorizontal_"+numStation)!=null)
        				this.goniometerHorizontal[numStation]=Double.parseDouble(properties.getProperty(prefix+"goniometerHorizontal_"+numStation));
        			if (properties.getProperty(prefix+"goniometerAxial_"+numStation)!=null)
        				this.goniometerAxial[numStation]=Double.parseDouble(properties.getProperty(prefix+"goniometerAxial_"+numStation));
        			if (properties.getProperty(prefix+"interAxisDistance_"+numStation)!=null)
        				this.interAxisDistance[numStation]=Double.parseDouble(properties.getProperty(prefix+"interAxisDistance_"+numStation));
        			if (properties.getProperty(prefix+"interAxisAngle_"+numStation)!=null)
        				this.interAxisAngle[numStation]=Double.parseDouble(properties.getProperty(prefix+"interAxisAngle_"+numStation));
        			if (properties.getProperty(prefix+"horAxisErrPhi_"+numStation)!=null)
        				this.horAxisErrPhi[numStation]=Double.parseDouble(properties.getProperty(prefix+"horAxisErrPhi_"+numStation));
        			if (properties.getProperty(prefix+"horAxisErrPsi_"+numStation)!=null)
        				this.horAxisErrPsi[numStation]=Double.parseDouble(properties.getProperty(prefix+"horAxisErrPsi_"+numStation));
        			if (properties.getProperty(prefix+"entrancePupilForward_"+numStation)!=null)
        				this.entrancePupilForward[numStation]=Double.parseDouble(properties.getProperty(prefix+"entrancePupilForward_"+numStation));
        			if (properties.getProperty(prefix+"centerAboveHorizontal_"+numStation)!=null)
        				this.centerAboveHorizontal[numStation]=Double.parseDouble(properties.getProperty(prefix+"centerAboveHorizontal_"+numStation));
        			if (this.GXYZ[numStation] == null) {
        				this.GXYZ[numStation] = new double[3];
        			}
        			if (properties.getProperty(prefix+"GXYZ_0_"+numStation)!=null)
        				this.GXYZ[numStation][0]=Double.parseDouble(properties.getProperty(prefix+"GXYZ_0_"+numStation));
        			if (properties.getProperty(prefix+"GXYZ_1_"+numStation)!=null)
        				this.GXYZ[numStation][1]=Double.parseDouble(properties.getProperty(prefix+"GXYZ_1_"+numStation));
        			if (properties.getProperty(prefix+"GXYZ_2_"+numStation)!=null)
        				this.GXYZ[numStation][2]=Double.parseDouble(properties.getProperty(prefix+"GXYZ_2_"+numStation));
        			if (numSubCameras>0) {
        				initSubCameras(numStation, numSubCameras); // set array with default parameters
        				for (int i=0;i<numSubCameras;i++){
        					this.eyesisSubCameras[numStation][i].getProperties(prefix+numStation+"_subCamera_"+i+'.',properties,i);
        				}
        			}
    			}
    		} else { // read old format
    			if (properties.getProperty(prefix+"goniometerHorizontal")!=null)
    				this.goniometerHorizontal[0]=Double.parseDouble(properties.getProperty(prefix+"goniometerHorizontal"));
    			if (properties.getProperty(prefix+"goniometerAxial")!=null)
    				this.goniometerAxial[0]=Double.parseDouble(properties.getProperty(prefix+"goniometerAxial"));
    			if (properties.getProperty(prefix+"interAxisDistance")!=null)
    				this.interAxisDistance[0]=Double.parseDouble(properties.getProperty(prefix+"interAxisDistance"));
    			if (properties.getProperty(prefix+"interAxisAngle")!=null)
    				this.interAxisAngle[0]=Double.parseDouble(properties.getProperty(prefix+"interAxisAngle"));
    			if (properties.getProperty(prefix+"horAxisErrPhi")!=null)
    				this.horAxisErrPhi[0]=Double.parseDouble(properties.getProperty(prefix+"horAxisErrPhi"));
    			if (properties.getProperty(prefix+"horAxisErrPsi")!=null)
    				this.horAxisErrPsi[0]=Double.parseDouble(properties.getProperty(prefix+"horAxisErrPsi"));
    			if (properties.getProperty(prefix+"entrancePupilForward")!=null)
    				this.entrancePupilForward[0]=Double.parseDouble(properties.getProperty(prefix+"entrancePupilForward"));
    			if (properties.getProperty(prefix+"centerAboveHorizontal")!=null)
    				this.centerAboveHorizontal[0]=Double.parseDouble(properties.getProperty(prefix+"centerAboveHorizontal"));
    			if (properties.getProperty(prefix+"GXYZ_0")!=null)
    				this.GXYZ[0][0]=Double.parseDouble(properties.getProperty(prefix+"GXYZ_0"));
    			if (properties.getProperty(prefix+"GXYZ_1")!=null)
    				this.GXYZ[0][1]=Double.parseDouble(properties.getProperty(prefix+"GXYZ_1"));
    			if (properties.getProperty(prefix+"GXYZ_2")!=null)
    				this.GXYZ[0][2]=Double.parseDouble(properties.getProperty(prefix+"GXYZ_2"));
    			System.out.println(" === numSubCameras="+numSubCameras);
    			if (numSubCameras>0) {
    				initSubCameras(0, numSubCameras); // set array with default parameters
    				for (int i=0;i<numSubCameras;i++){
    					System.out.println("this.eyesisSubCameras[0]["+i+"].getProperties("+prefix+"subCamera_"+i+".,properties);");
    					this.eyesisSubCameras[0][i].getProperties(prefix+"subCamera_"+i+'.',properties);
    				}
    			}
    		}
//    		getCostsProperties(prefix,properties);
    	}
    	public void  updateNumstations (int newNumStations){
//    		System.out.println("updateNumstations("+newNumStations+"), was "+this.numStations);
    		if (newNumStations==this.numStations) return;
    		System.out.println("updateNumstations("+newNumStations+"), was "+this.numStations);
    		EyesisCameraParameters newData=this.clone();
    		copyData(
    				newNumStations,
    				newData,
        			this);
    		System.out.println("updateNumstations() after copyData() this.numStations="+this.numStations);
    	}

    	// returns -1 - canceled, 0 - done, >0 - number of sub-camera to edit

    	private int subShowDialog(String title, int nextSubCamera) {
    		GenericDialog gd = new GenericDialog(title);
//        	public double goniometerHorizontal; // goniometer rotation around "horizontal" axis (tilting from the target - positive)
//        	public double goniometerAxial; // goniometer rotation around Eyesis axis (clockwise in plan - positive
//this.isTripod
    		gd.addCheckbox("Cartesian coordinates for subcamera (false - cylindrical)",                     this.cartesian);
    		String [] modelChoice=new String [distortionModelDescriptions.length+1];
    		modelChoice[0]="--- keep current ---";
    		for (int i=0;i<distortionModelDescriptions.length;i++) modelChoice[i+1]=distortionModelDescriptions[i];
    		gd.addChoice("Change camera distortion model for all channels", modelChoice, modelChoice[0]);
    		gd.addCheckbox("Tripod mode (first vertical axis, then horizontal), changes meaning of the next 2 fields",this.is_tripod);
    		gd.addMessage("=== Camera parameters to be fitted ===");
    		for (int numStation=0;numStation<this.numStations;numStation++) {
    			if (this.numStations>1){
    				gd.addMessage("--- Station number "+numStation+" ---");
    				gd.addNumericField("Station reprojection errors weight",      100*this.stationWeight[numStation], 1,5,"%");
    			}
    			if (this.is_tripod) {
    				gd.addNumericField("Tripod rotation around vertical axis (clockwise from top - positive)",this.goniometerHorizontal[numStation], 3,7,"degrees");
    				gd.addNumericField("Tripod rotation around horizontal axis (camera up - positive)",       this.goniometerAxial[numStation], 3,7,"degrees");
    			} else {
    				gd.addNumericField("Goniometer rotation around 'horizontal' axis (tilting from the target - positive)",this.goniometerHorizontal[numStation], 3,7,"degrees");
    				gd.addNumericField("Rotation around Eyesis main axis (clockwise in plan - positive)",      this.goniometerAxial[numStation], 3,7,"degrees");
    			}

    			gd.addNumericField("Distance between goniometer axes",                                     this.interAxisDistance[numStation], 3,7,"mm");
    			gd.addNumericField("Angle error between goniometer axes (<0 if vertical axis rotated CW )",this.interAxisAngle[numStation],  3,7,"degrees");
    			if (this.is_tripod) {
    				gd.addNumericField("Vertical tripod axis tilt  from true vertical",                    this.horAxisErrPhi[numStation],  3,7,"degrees");
    				gd.addNumericField("Vertical tripod axis roll error from true vertical",               this.horAxisErrPsi[numStation],  3,7,"degrees");
    			} else {
    				gd.addNumericField("Horizontal axis azimuth error (CW in plan)",                       this.horAxisErrPhi[numStation],  3,7,"degrees");
    				gd.addNumericField("Horizontal axis roll error (CW looking to target)",                this.horAxisErrPsi[numStation],  3,7,"degrees");
    			}

    			gd.addNumericField("Distance between the lens entrace pupil and the sensor",               this.entrancePupilForward[numStation],  3,7,"mm");
    			gd.addNumericField("Camera center above goniometer horizontal axis",                       this.centerAboveHorizontal[numStation],  3,7,"mm");

    			gd.addNumericField("Goniometer reference point position X (target coordinates, left)",     this.GXYZ[numStation][0], 3,7,"mm");
    			gd.addNumericField("Goniometer reference point position Y (target coordinates, up)",       this.GXYZ[numStation][1], 3,7,"mm");
    			gd.addNumericField("Goniometer reference point position Z (target coordinates, away)",     this.GXYZ[numStation][2], 3,7,"mm");
    		}
    		gd.addMessage("=== Other parameters ===");

    		gd.addNumericField("Image sensor width (maximal if different)",                            this.defaultSensorWidth, 0,4,"pix");
    		gd.addNumericField("Image sensor height (maximal if different)",                           this.defaultSensorHeight, 0,4,"pix");
    		gd.addNumericField("Shrink detected grid by this number of nodes (half/periods) for masks",this.shrinkGridForMask, 0,4,"grid nodes");
    		gd.addNumericField("Gaussian blur masks for the sensors (positive - pixels, negative - grid half-periods)", this.maskBlurSigma, 2,6,"pix");
    		gd.addNumericField("Reduce sensor resolution when calculating masks",                      this.defaultDecimateMasks, 0);

    		gd.addNumericField("Filter out grid nodes with difference from quadratically predicted from 8 neighbors", this.badNodeThreshold, 2,6,"pix");
    		gd.addNumericField("Maximal number of bad nodes around the corrected one to fix",          this.maxBadNeighb, 0);
    		gd.addNumericField("Minimal number of valid (with all filters applied) nodes in each image",this.minimalValidNodes, 0);
    		gd.addNumericField("Increase weight of the multi-image sets (0 - do not increase, 1 - multiply by number of images in a set (to power ), 2 - same but remove single-image ",this.weightMultiImageMode, 0);
    		gd.addNumericField("Increase weight of the multi-image sets power (used with mode above)", this.weightMultiExponent, 2,6,"");
    		gd.addNumericField("Increase weight of the images by the power of their diameters", this.weightDiameterExponent, 2,6,"");
    		gd.addNumericField("Increase weight Y-error with respect to X-error",                100.0*this.weightYtoX, 2,6,"%");
    		gd.addNumericField("Filter out grid nodes with the contrast less than this value (maximal is ~0.8) ", this.minimalGridContrast, 2,6,"");
    		gd.addNumericField("Shrink-blur detected grids alpha",                                     this.shrinkBlurSigma, 2,6,"grid nodes");
    		gd.addNumericField("Shrink-blur detected grids  level (-1..+1)",                           this.shrinkBlurLevel, 2,6,"");
    		gd.addNumericField("Channel balace mode: <0 - use specified defaults, 0 - keep curent, >0 - exponent for correction (1.0 - precise equalization)", this.balanceChannelWeightsMode, 3,6,"");
    		gd.addNumericField("Remove nodes with error greater than scaled RMS in that image, weighted",  this.removeOverRMS, 2,6,"xRMS");
    		gd.addNumericField("Same, not weghted (not more permissive near the borders with low weight)", this.removeOverRMSNonweighted, 2,6,"xRMS");
    		gd.addCheckbox    ("Unmared grid files for LWIR is saved invereted",                       this.invertUnmarkedLwirGrid);
    		gd.addNumericField("Number of sub-camera modules",                                         this.eyesisSubCameras[0].length,   0,2,"");
    		gd.addNumericField("Number of sub-camera module to edit (<=0  - none)",                    nextSubCamera,   0,2,"");
   	        gd.enableYesNoCancel("OK", "Done");
    	    WindowTools.addScrollBars(gd);
    		gd.showDialog();
    		if (gd.wasCanceled()) return -1;
    		boolean newCartesian = gd.getNextBoolean();
    		int modelIndex=gd.getNextChoiceIndex()-1;
    		if (modelIndex>=0){
    			for (EyesisSubCameraParameters [] esps:this.eyesisSubCameras){
    				for (EyesisSubCameraParameters esp:esps){
    					esp.lensDistortionModel=distortionModels[modelIndex];
    				}
    			}
    		}

    		this.is_tripod=                 gd.getNextBoolean();
    		for (int numStation=0;numStation<this.numStations;numStation++) {
    			if (this.numStations>1){
    				this.stationWeight[numStation]=0.01*gd.getNextNumber();
    			}
    			this.goniometerHorizontal[numStation]=     gd.getNextNumber();
    			this.goniometerAxial[numStation]=          gd.getNextNumber();
    			this.interAxisDistance[numStation]=        gd.getNextNumber();
    			this.interAxisAngle[numStation]=           gd.getNextNumber();
    			this.horAxisErrPhi[numStation]=            gd.getNextNumber();
    			this.horAxisErrPsi[numStation]=            gd.getNextNumber();
    			this.entrancePupilForward[numStation]=     gd.getNextNumber();
    			this.centerAboveHorizontal[numStation]=    gd.getNextNumber();

    			this.GXYZ[numStation][0]=                  gd.getNextNumber();
    			this.GXYZ[numStation][1]=                  gd.getNextNumber();
    			this.GXYZ[numStation][2]=                  gd.getNextNumber();
    		}
    		this.defaultSensorWidth=  (int) gd.getNextNumber();
    		this.defaultSensorHeight= (int) gd.getNextNumber();
    		this.shrinkGridForMask=   (int) gd.getNextNumber();
    		this.maskBlurSigma=             gd.getNextNumber();
    		this.defaultDecimateMasks=(int) gd.getNextNumber();
	    	this.badNodeThreshold=          gd.getNextNumber();
	    	this.maxBadNeighb=        (int) gd.getNextNumber();
	    	this.minimalValidNodes=   (int) gd.getNextNumber();
	    	this.weightMultiImageMode=(int) gd.getNextNumber();
    		this.weightMultiExponent=       gd.getNextNumber();
    		this.weightDiameterExponent=    gd.getNextNumber();
    		this.weightYtoX=           0.01*gd.getNextNumber();
	    	this.minimalGridContrast=       gd.getNextNumber();
	    	this.shrinkBlurSigma =          gd.getNextNumber();
	    	this.shrinkBlurLevel =          gd.getNextNumber();
	    	this.balanceChannelWeightsMode= gd.getNextNumber();
	    	this.removeOverRMS =            gd.getNextNumber();
	    	this.removeOverRMSNonweighted=  gd.getNextNumber();
	    	this.invertUnmarkedLwirGrid =   gd.getNextBoolean();
    		int numSubCams=           (int) gd.getNextNumber();
    		int numSubCam=            (int) gd.getNextNumber();
    		if (numSubCams!=this.eyesisSubCameras[0].length){
    			this.eyesisSubCameras=new EyesisSubCameraParameters[this.numStations][];
    	    	for (int numStation=0;numStation<numStations;numStation++) {
    	    		initSubCameras(numStation,numSubCams); // re-initialize from defaults, discard current!
    	    	}
    		}
    		if (numSubCam<0)numSubCam=0;
    		if (newCartesian != this.cartesian){
    			this.cartesian = newCartesian;
    			updateCartesian();
    		}

    		return gd.wasOKed()?numSubCam:-1;
    	}

    	public boolean showSubcameraDialog(
    			int numSubCam,
    			String title) {
    		GenericDialog gd = new GenericDialog(title);
    		if (this.numStations > 1) gd.addCheckbox    ("Propagate first station parameters to other stations ",false);
    		for (int numStation=0;numStation<this.numStations;numStation++) {
    			EyesisSubCameraParameters subCam=this.eyesisSubCameras[numStation][numSubCam];
    			if (subCam!=null) {
		    		gd.addMessage("Channel weight "+subCam.channelWeightCurrent);
    				if (numStation==0){
        				gd.addNumericField("Channel "+numSubCam+" default weight",      subCam.channelWeightDefault, 5,8,"");
    				}
    				if (this.numStations>1) gd.addMessage("--- Station number "+numStation+" ---");
    				gd.addNumericField("Subcamera lens distortion model",               subCam.lensDistortionModel, 8,0,"");
    				gd.addCheckbox    ("Enable matching w/o laser pointers",            subCam.enableNoLaser);
    				if (numStation == 0) {
    					gd.addNumericField("Image sensor width",                            this.getSensorWidth (numSubCam), 0,4,"pix");
    					gd.addNumericField("Image sensor height",                           this.getSensorHeight(numSubCam), 0,4,"pix");
    				}
    				if (subCam.cartesian) {
    					gd.addNumericField("Subcamera right from the axis",             subCam.right, 5,9,"mm");
    					gd.addNumericField("Subcamera forward from the rotation axis",  subCam.forward,  5,9,"mm");
    				} else {
    					gd.addNumericField("Subcamera azimuth",                         subCam.azimuth, 5,9,"degrees");
    					gd.addNumericField("Subcamera distance from the axis",          subCam.radius,  5,9,"mm");
    				}
    				gd.addNumericField("Subcamera height from the 'equator'",           subCam.height,  5,9,"mm");

    				if (subCam.cartesian) {
    					gd.addNumericField("Optical axis heading (absulute, CW positive)", subCam.heading,     5,9,"degrees");
    				} else {
    					gd.addNumericField("Optical axis heading (relative to azimuth)",    subCam.phi,     5,9,"degrees");
    				}
    				gd.addNumericField("Optical axis elevation (up from equator)",      subCam.theta,   5,9,"degrees");
    				gd.addNumericField("Camera roll, positive CW looking to the target",subCam.psi,     5,9,"degrees");
    				gd.addNumericField("Lens focal length",               subCam.focalLength, 5,8,"mm");
    				gd.addNumericField("Sensor pixel period",             subCam.pixelSize, 5,8,"um");
    				gd.addNumericField("Distortion radius (half width)",  subCam.distortionRadius, 6,8,"mm");
    				gd.addNumericField("Distortion A8 (r^8)",             subCam.distortionA8, 8,10,"");
    				gd.addNumericField("Distortion A7 (r^7)",             subCam.distortionA7, 8,10,"");
    				gd.addNumericField("Distortion A6 (r^6)",             subCam.distortionA6, 8,10,"");
    				gd.addNumericField("Distortion A5 (r^5)",             subCam.distortionA5, 8,10,"");
    				gd.addNumericField("Distortion A (r^4)",              subCam.distortionA, 8,10,"");
    				gd.addNumericField("Distortion B (r^3)",              subCam.distortionB, 8,10,"");
    				gd.addNumericField("Distortion C (r^2)",              subCam.distortionC, 8,10,"");
    				gd.addNumericField("Lens axis on the sensor (horizontal, from left edge)", subCam.px0, 4,9,"pixels");
    				gd.addNumericField("Lens axis on the sensor (vertical, from top  edge)",   subCam.py0, 4,9,"pixels");

    				gd.addMessage("=== non-radial model parameters ===");
    				gd.addMessage("For r^2 (Distortion C):");
    				gd.addNumericField("Orthogonal elongation for r^2",   100*subCam.r_od[0][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^2",     100*subCam.r_od[0][1], 8,10,"%");
    				gd.addMessage("For r^3 (Distortion B):");
    				gd.addNumericField("Distortion center shift X for r^3", 100*subCam.r_xy[0][0], 8,10,"%");
    				gd.addNumericField("Distortion center shift Y for r^3", 100*subCam.r_xy[0][1], 8,10,"%");
    				gd.addNumericField("Orthogonal elongation for r^3",  100*subCam.r_od[1][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^3",    100*subCam.r_od[1][1], 8,10,"%");
    				gd.addMessage("For r^4 (Distortion A):");
    				gd.addNumericField("Distortion center shift X for r^4", 100*subCam.r_xy[1][0], 8,10,"%");
    				gd.addNumericField("Distortion center shift Y for r^4", 100*subCam.r_xy[1][1], 8,10,"%");
    				gd.addNumericField("Orthogonal elongation for r^4",  100*subCam.r_od[2][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^4",    100*subCam.r_od[2][1], 8,10,"%");
    				gd.addMessage("For r^5 (Distortion A5):");
    				gd.addNumericField("Distortion center shift X for r^5", 100*subCam.r_xy[2][0], 8,10,"%");
    				gd.addNumericField("Distortion center shift Y for r^5", 100*subCam.r_xy[2][1], 8,10,"%");
    				gd.addNumericField("Orthogonal elongation for r^5",  100*subCam.r_od[3][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^5",    100*subCam.r_od[3][1], 8,10,"%");
    				gd.addMessage("For r^6 (Distortion A6:");
    				gd.addNumericField("Distortion center shift X for r^6", 100*subCam.r_xy[3][0], 8,10,"%");
    				gd.addNumericField("Distortion center shift Y for r^6", 100*subCam.r_xy[3][1], 8,10,"%");
    				gd.addNumericField("Orthogonal elongation for r^6",  100*subCam.r_od[4][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^6",    100*subCam.r_od[4][1], 8,10,"%");
    				gd.addMessage("For r^7 (Distortion A7):");
    				gd.addNumericField("Distortion center shift X for r^7", 100*subCam.r_xy[4][0], 8,10,"%");
    				gd.addNumericField("Distortion center shift Y for r^7", 100*subCam.r_xy[4][1], 8,10,"%");
    				gd.addNumericField("Orthogonal elongation for r^7",  100*subCam.r_od[5][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^7",    100*subCam.r_od[5][1], 8,10,"%");
    				gd.addMessage("For r^8 (Distortion A8):");
    				gd.addNumericField("Distortion center shift X for r^8", 100*subCam.r_xy[5][0], 8,10,"%");
    				gd.addNumericField("Distortion center shift Y for r^8", 100*subCam.r_xy[5][1], 8,10,"%");
    				gd.addNumericField("Orthogonal elongation for r^8",  100*subCam.r_od[6][0], 8,10,"%");
    				gd.addNumericField("Diagonal elongation for r^8",    100*subCam.r_od[6][1], 8,10,"%");

    			}
    		}
   	        gd.enableYesNoCancel("OK", "Done");
    	    WindowTools.addScrollBars(gd);
    		gd.showDialog();
    		if (gd.wasCanceled()) return false;
    		double channelWeightDefault=1.0;
    		boolean propagateFirstStation = (this.numStations > 1) ? gd.getNextBoolean() : false;
    		for (int numStation=0;numStation<this.numStations;numStation++) {
    			EyesisSubCameraParameters subCam=this.eyesisSubCameras[numStation][numSubCam];
    			if (subCam!=null) {
    				if (numStation==0){
    		    		gd.addMessage("Channel weight "+subCam.channelWeightCurrent);
    		    		channelWeightDefault=gd.getNextNumber();
    				}
    				subCam.channelWeightDefault= channelWeightDefault; // assign to all stations
    				subCam.lensDistortionModel= (int) gd.getNextNumber();
    				subCam.enableNoLaser =            gd.getNextBoolean();
    				if (numStation == 0) {
    					this.setSensorWidth (numSubCam,(int) gd.getNextNumber());
    					this.setSensorHeight(numSubCam,(int) gd.getNextNumber());
    				}
    				if (subCam.cartesian) {
    					subCam.right=       gd.getNextNumber();
    					subCam.forward=     gd.getNextNumber();
    				} else {
    					subCam.azimuth=     gd.getNextNumber();
    					subCam.radius=      gd.getNextNumber();
    				}
    				subCam.height=          gd.getNextNumber();
    				if (subCam.cartesian) {
    					subCam.heading=     gd.getNextNumber();
    				}else {
    					subCam.phi=         gd.getNextNumber();
    				}
    				subCam.theta=           gd.getNextNumber();
    				subCam.psi=             gd.getNextNumber();
    				subCam.focalLength=     gd.getNextNumber();
    				subCam.pixelSize=       gd.getNextNumber();
    				subCam.distortionRadius=gd.getNextNumber();
    				subCam.distortionA8=    gd.getNextNumber();
    				subCam.distortionA7=    gd.getNextNumber();
    				subCam.distortionA6=    gd.getNextNumber();
    				subCam.distortionA5=    gd.getNextNumber();
    				subCam.distortionA=     gd.getNextNumber();
    				subCam.distortionB=     gd.getNextNumber();
    				subCam.distortionC=     gd.getNextNumber();
    				subCam.px0=             gd.getNextNumber();
    				subCam.py0=             gd.getNextNumber();

    				subCam.r_od[0][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[0][1]= 0.01*gd.getNextNumber();
    				subCam.r_xy[0][0]= 0.01*gd.getNextNumber();
    				subCam.r_xy[0][1]= 0.01*gd.getNextNumber();
    				subCam.r_od[1][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[1][1]= 0.01*gd.getNextNumber();
    				subCam.r_xy[1][0]= 0.01*gd.getNextNumber();
    				subCam.r_xy[1][1]= 0.01*gd.getNextNumber();
    				subCam.r_od[2][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[2][1]= 0.01*gd.getNextNumber();
    				subCam.r_xy[2][0]= 0.01*gd.getNextNumber();
    				subCam.r_xy[2][1]= 0.01*gd.getNextNumber();
    				subCam.r_od[3][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[3][1]= 0.01*gd.getNextNumber();
    				subCam.r_xy[3][0]= 0.01*gd.getNextNumber();
    				subCam.r_xy[3][1]= 0.01*gd.getNextNumber();
    				subCam.r_od[4][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[4][1]= 0.01*gd.getNextNumber();
    				subCam.r_xy[4][0]= 0.01*gd.getNextNumber();
    				subCam.r_xy[4][1]= 0.01*gd.getNextNumber();
    				subCam.r_od[5][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[5][1]= 0.01*gd.getNextNumber();
    				subCam.r_xy[5][0]= 0.01*gd.getNextNumber();
    				subCam.r_xy[5][1]= 0.01*gd.getNextNumber();
    				subCam.r_od[6][0]= 0.01*gd.getNextNumber();
    				subCam.r_od[6][1]= 0.01*gd.getNextNumber();
    			}
    		}
    		if (propagateFirstStation){
    			EyesisSubCameraParameters first=this.eyesisSubCameras[0][numSubCam];
    			if (first != null){
    	    		for (int numStation=1;numStation<this.numStations;numStation++) {
    	    			EyesisSubCameraParameters subCam=this.eyesisSubCameras[numStation][numSubCam];
    	    			if (subCam!=null) {
    	    	    		this.setSensorWidth (numSubCam,first.getSensorWidth  ());
    	    	    		this.setSensorHeight(numSubCam,first.getSensorHeight ());

    	    				if (subCam.cartesian) {
    	    					subCam.right=       first.right;
    	    					subCam.forward=     first.forward;
    	    				} else {
    	    					subCam.azimuth=     first.azimuth;
    	    					subCam.radius=      first.radius;
    	    				}
    	    				subCam.height=          first.height;
    	    				if (subCam.cartesian) {
    	    					subCam.heading=     first.heading;
    	    				}else {
    	    					subCam.phi=         first.phi;
    	    				}
    	    				subCam.theta=           first.theta;
    	    				subCam.psi=             first.psi;
    	    				subCam.focalLength=     first.focalLength;
    	    				subCam.pixelSize=       first.pixelSize;
    	    				subCam.distortionRadius=first.distortionRadius;
    	    				subCam.distortionA8=    first.distortionA8;
    	    				subCam.distortionA7=    first.distortionA7;
    	    				subCam.distortionA6=    first.distortionA6;
    	    				subCam.distortionA5=    first.distortionA5;
    	    				subCam.distortionA=     first.distortionA;
    	    				subCam.distortionB=     first.distortionB;
    	    				subCam.distortionC=     first.distortionC;
    	    				subCam.px0=             first.px0;
    	    				subCam.py0=             first.py0;

    	    				subCam.r_od[0][0]= subCam.r_od[0][0];
    	    				subCam.r_od[0][1]= subCam.r_od[0][1];
    	    				subCam.r_xy[0][0]= subCam.r_xy[0][0];
    	    				subCam.r_xy[0][1]= subCam.r_xy[0][1];
    	    				subCam.r_od[1][0]= subCam.r_od[1][0];
    	    				subCam.r_od[1][1]= subCam.r_od[1][1];
    	    				subCam.r_xy[1][0]= subCam.r_xy[1][0];
    	    				subCam.r_xy[1][1]= subCam.r_xy[1][1];
    	    				subCam.r_od[2][0]= subCam.r_od[2][0];
    	    				subCam.r_od[2][1]= subCam.r_od[2][1];
    	    				subCam.r_xy[2][0]= subCam.r_xy[2][0];
    	    				subCam.r_xy[2][1]= subCam.r_xy[2][1];
    	    				subCam.r_od[3][0]= subCam.r_od[3][0];
    	    				subCam.r_od[3][1]= subCam.r_od[3][1];
    	    				subCam.r_xy[3][0]= subCam.r_xy[3][0];
    	    				subCam.r_xy[3][1]= subCam.r_xy[3][1];
    	    				subCam.r_od[4][0]= subCam.r_od[4][0];
    	    				subCam.r_od[4][1]= subCam.r_od[4][1];
    	    				subCam.r_xy[4][0]= subCam.r_xy[4][0];
    	    				subCam.r_xy[4][1]= subCam.r_xy[4][1];
    	    				subCam.r_od[5][0]= subCam.r_od[5][0];
    	    				subCam.r_od[5][1]= subCam.r_od[5][1];
    	    				subCam.r_xy[5][0]= subCam.r_xy[5][0];
    	    				subCam.r_xy[5][1]= subCam.r_xy[5][1];
    	    				subCam.r_od[6][0]= subCam.r_od[6][0];
    	    				subCam.r_od[6][1]= subCam.r_od[6][1];
    	    			}
    	    		}
    			}
    		}
    		for (int numStation=0;numStation<this.numStations;numStation++) {
    			EyesisSubCameraParameters subCam=this.eyesisSubCameras[numStation][numSubCam];
    			if (subCam!=null) {
    				subCam.updateCartesian();
    			}
    		}
    		return gd.wasOKed();
    	}

    	// TODO: make subcameras show numbers from 0
    	public boolean showDialog(String title) {
    		System.out.println("this.eyesisSubCameras.length="+this.eyesisSubCameras.length);
    		System.out.println("this.eyesisSubCameras[0].length="+this.eyesisSubCameras[0].length);
    		int subCam=1;
    		while (subCam<=this.eyesisSubCameras[0].length) {
        		System.out.println("subCam="+subCam);
    			subCam=subShowDialog(title, subCam);
    			if (subCam<0) return false;
    			if (subCam==0) return true;
    			while (subCam<=this.eyesisSubCameras[0].length) {
//    				if (!this.eyesisSubCameras[subCam-1].showDialog(title+": subcamera "+subCam)) break;
    				if (!showSubcameraDialog(subCam-1,title+": subcamera "+subCam+" ("+(subCam-1)+")")) break;
    				subCam++;
    			}
    		}
    		return true;
    	}
    	public int getLensDistortionModel(int stationNumber,int subCamNumber){
        	if (
        			(this.eyesisSubCameras==null) ||
        			(this.numStations<=stationNumber) ||
        			(this.eyesisSubCameras.length<=stationNumber) ||
        			(this.eyesisSubCameras[stationNumber].length<=subCamNumber)) throw new IllegalArgumentException
            ("Nonexistent subcamera "+subCamNumber+ " and/or station number="+stationNumber+" this.numStations="+this.numStations+" this.eyesisSubCameras.length="+this.eyesisSubCameras.length);
        	EyesisSubCameraParameters subCam=this.eyesisSubCameras[stationNumber][subCamNumber];
        	return subCam.lensDistortionModel;
    	}
    	public boolean getEnableNoLaser(int stationNumber,int subCamNumber){
        	if (
        			(this.eyesisSubCameras==null) ||
        			(this.numStations<=stationNumber) ||
        			(this.eyesisSubCameras.length<=stationNumber) ||
        			(this.eyesisSubCameras[stationNumber].length<=subCamNumber)) throw new IllegalArgumentException
            ("Nonexistent subcamera "+subCamNumber+ " and/or station number="+stationNumber+" this.numStations="+this.numStations+" this.eyesisSubCameras.length="+this.eyesisSubCameras.length);
        	EyesisSubCameraParameters subCam=this.eyesisSubCameras[stationNumber][subCamNumber];
        	return subCam.enableNoLaser;
    	}
        /**
         *
         * @param eyesisCameraParameters current parameters of the Eyesis camera, subcameras and goniometer
         * @param subCamNumber number of sub-camera (from 0)
         * @return array of the parameters (both individual sub-camera and common to all sub-cameras)
         *
         */
        public double [] getParametersVector(
        		int stationNumber,
        		int subCamNumber //
                ){
        	if (
        			(this.eyesisSubCameras==null) ||
        			(this.numStations<=stationNumber) ||
        			(this.eyesisSubCameras.length<=stationNumber) ||
        			(this.eyesisSubCameras[stationNumber].length<=subCamNumber)) throw new IllegalArgumentException
            ("Nonexistent subcamera "+subCamNumber+ " and/or station number="+stationNumber+" this.numStations="+this.numStations+" this.eyesisSubCameras.length="+this.eyesisSubCameras.length);

        	EyesisSubCameraParameters subCam=this.eyesisSubCameras[stationNumber][subCamNumber];
//        	System.out.println("getParametersVector("+stationNumber+","+subCamNumber+"), subCam is "+((subCam==null)?"null":"NOT null"));
        	double [] parVect={
        			(subCam.cartesian?subCam.right:  subCam.azimuth),            // 0 azimuth of the lens entrance pupil center, degrees, clockwise looking from top
        		    (subCam.cartesian?subCam.forward:subCam.radius),             // 1 mm, distance from the rotation axis
        			subCam.height,                                               // 2 mm, up (was downwards?) - from the origin point
        			(subCam.cartesian?subCam.heading:subCam.phi),                // 3 degrees, optical axis from azimuth/r vector, clockwise
        			subCam.theta,                                                // 4 degrees, optical axis from the eyesis horizon, positive - up
        			subCam.psi,                                                  // 5 degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
        			this.goniometerHorizontal[stationNumber], // 6 goniometer rotation around "horizontal" axis (tilting from the target - positive)
        			this.goniometerAxial[stationNumber],      // 7 goniometer rotation around Eyesis axis (clockwise in plan - positive
        			this.interAxisDistance[stationNumber],    // 8 distance in mm between two goniometer axes
        			this.interAxisAngle[stationNumber],       // 9 angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
        			this.horAxisErrPhi[stationNumber],        //10 angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
        			this.horAxisErrPsi[stationNumber],        //11 angle in degrees "horizontal" goniometer axis is rotated around moving X axis (up)
            		this.entrancePupilForward[stationNumber], //12 common to all lenses - distance from the sensor to the lens entrance pupil
            		this.centerAboveHorizontal[stationNumber],//13 camera center distance along camera axis above the closest point to horizontal rotation axis (adds to height of each

        			this.GXYZ[stationNumber][0],              //14 (12) coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
        			this.GXYZ[stationNumber][1],              //15 (13)  y
        			this.GXYZ[stationNumber][2],              //16 (14)  z
        			subCam.focalLength,        //17 (15)  lens focal length
        			subCam.px0,                //18 (16) center of the lens on the sensor, pixels
        			subCam.py0,                //19 (17) center of the lens on the sensor, pixels
        			subCam.distortionA8,       //20 (18)  r^8 (normalized to focal length or to sensor half width?)
        			subCam.distortionA7,       //21 (19)  r^7 (normalized to focal length or to sensor half width?)
        			subCam.distortionA6,       //22 (20)  r^6 (normalized to focal length or to sensor half width?)
        			subCam.distortionA5,       //23 (21)  r^5 (normalized to focal length or to sensor half width?)
        			subCam.distortionA,        //24 (22)  r^4 (normalized to focal length or to sensor half width?)
        			subCam.distortionB,        //25 (23)  r^3
        			subCam.distortionC,        //26 (24)  r^2
        			subCam.r_od[0][0],
        			subCam.r_od[0][1],
        			subCam.r_xy[0][0],
        			subCam.r_xy[0][1],
        			subCam.r_od[1][0],
        			subCam.r_od[1][1],
        			subCam.r_xy[1][0],
        			subCam.r_xy[1][1],
        			subCam.r_od[2][0],
        			subCam.r_od[2][1],
        			subCam.r_xy[2][0],
        			subCam.r_xy[2][1],
        			subCam.r_od[3][0],
        			subCam.r_od[3][1],
        			subCam.r_xy[3][0],
        			subCam.r_xy[3][1],
        			subCam.r_od[4][0],
        			subCam.r_od[4][1],
        			subCam.r_xy[4][0],
        			subCam.r_xy[4][1],
        			subCam.r_od[5][0],
        			subCam.r_od[5][1],
        			subCam.r_xy[5][0],
        			subCam.r_xy[5][1],
        			subCam.r_od[6][0],
        			subCam.r_od[6][1]
        	};
        	// Global parameters, not adjusted - just copied once when camera is selected
    // or should they stay fixed and not copied at all?
//    		this.lensDistortionParameters.pixelSize=subCam.pixelSize; // has to be set separately
//    		this.lensDistortionParameters.distortionRadius=subCam.distortionRadius;
        	return parVect;
        }

 //       public int getNumSubCameras (){return (this.eyesisSubCameras==null)?0:this.eyesisSubCameras.length;}
        public int getGoniometerHorizontalIndex(){return 6;}
        public int getGoniometerAxialIndex(){return 7;}
        public int getInterAxisAngleIndex(){return 9;}
@Deprecated
        public int getSensorWidth()         { return this.defaultSensorWidth;}
@Deprecated
        public int getSensorHeight()        { return this.defaultSensorHeight;}
@Deprecated
        public int getDecimateMasks()       { return this.defaultDecimateMasks;}
@Deprecated
        public void setSensorWidth(int v)   { this.defaultSensorWidth = v;}
@Deprecated
        public void setSensorHeight(int v)  { this.defaultSensorHeight = v;}
@Deprecated
        public void setDecimateMasks(int v) { this.defaultDecimateMasks = v;}


        public int getSensorWidth(int subCam) { return this.eyesisSubCameras[0][subCam].getSensorWidth();} // for the future? different sensors
        public int getSensorHeight(int subCam) { return this.eyesisSubCameras[0][subCam].getSensorHeight();}// for the future? different sensors
        public int [] getSensorWidthHeight(int subCam) {
        	int [] wh = {getSensorWidth( subCam), getSensorHeight( subCam)};
        	return wh;
        }

        public int getDecimateMasks(int subCam) { return this.eyesisSubCameras[0][subCam].getDecimateMasks();}// for the future? different sensors

        public void setSensorWidth(int subCam, int v)   {
        	for (int station = 0; station < this.eyesisSubCameras.length; station++) {
        		this.eyesisSubCameras[station][subCam].setSensorWidth(v);
        	}
        }
        public void setSensorHeight(int subCam, int v)  {
        	for (int station = 0; station < this.eyesisSubCameras.length; station++) {
        		this.eyesisSubCameras[station][subCam].setSensorHeight(v);
        	}
        }
        public void setDecimateMasks(int subCam, int v) {
        	for (int station = 0; station < this.eyesisSubCameras.length; station++) {
        		this.eyesisSubCameras[station][subCam].setDecimateMasks(v);
        	}
        }

        public int [] getSensorWidths() {
        	int [] v = new int [eyesisSubCameras[0].length];
        	for (int subCam = 0; subCam < v.length; subCam++) v[subCam] = getSensorWidth(subCam);
        	return v;
        } // for the future? different sensors
        public int [] getSensorHeights() {
        	int [] v = new int [eyesisSubCameras[0].length];
        	for (int subCam = 0; subCam < v.length; subCam++) v[subCam] = getSensorHeight(subCam);
        	return v;

        }// for the future? different sensors
        public int [] getAllMasks() {
        	int [] v = new int [eyesisSubCameras[0].length];
        	for (int subCam = 0; subCam < v.length; subCam++) v[subCam] = getDecimateMasks(subCam);
        	return v;
        }// for the future? different sensors


        public double getPixelSize(int subCamNumber){return  this.eyesisSubCameras[0][subCamNumber].pixelSize;} // use station 0's pixel size
        public double getDistortionRadius(int subCamNumber){return  this.eyesisSubCameras[0][subCamNumber].distortionRadius;}

        public void setParametersVectorAllStations(
        		double [] parVect,
        		boolean [] update,
        		int subCamNumber //
                ){
        	for (int stationNumber=0;stationNumber<this.numStations;stationNumber++){
        		setParametersVector(
                		parVect,
                		update,
                		stationNumber,
                		subCamNumber );
        	}

        }

        /**
         * Set camera/subcamera parameters from the parameters vector
         * @param parVect array of the parameters (both individual sub-camera and common to all sub-cameras)
         * @param update - which parameter of the vector to update
         * @param eyesisCameraParameters current parameters of the Eyesis camera, subcameras and goniometer
         * @param subCamNumber number of sub-camera (from 0)
         */
        public void setParametersVector(
        		double [] parVect,
        		boolean [] update,
        		int stationNumber,
        		int subCamNumber //
                ){
//        	if (parVect.length!=27) throw new IllegalArgumentException ("Wrong length of the parameters vector: "+parVect.length+"(should be 27)");
        	if (parVect.length!=53) throw new IllegalArgumentException ("Wrong length of the parameters vector: "+parVect.length+"(should be 53)");
        	if (
        			(this.eyesisSubCameras==null) ||
        			(this.numStations<=stationNumber) ||
        			(this.eyesisSubCameras.length<=stationNumber) ||
        			(this.eyesisSubCameras[stationNumber].length<=subCamNumber)) throw new IllegalArgumentException
            ("Nonexistent subcamera "+subCamNumber+ " and/or station number="+stationNumber);
        	EyesisSubCameraParameters subCam=this.eyesisSubCameras[stationNumber][subCamNumber];
        	if (subCam.cartesian){
        		if (update[0]) subCam.right=parVect[0];              // 0 mm, right of the camera axis from goniometer vertical rotation center to the target
        		if (update[1]) subCam.forward=parVect[1];            // 1 mm, forward from the goniometer vertical rotation center
        	} else {
        		if (update[0]) subCam.azimuth=parVect[0];            // 0 azimuth of the lens entrance pupil center, degrees, clockwise looking from top
        		if (update[1]) subCam.radius=parVect[1];             // 1 mm, distance from the rotation axis
        	}
        	if (update[2]) subCam.height=parVect[2];                 // 2 mm, up (was downwards?) - from the origin point
        	if (subCam.cartesian){
        		if (update[2]) subCam.heading=parVect[3];            // 3 degrees, optical axis from Z (to target), clockwise
        	} else {
        		if (update[2]) subCam.phi=parVect[3];                // 3 degrees, optical axis from azimuth/r vector, clockwise
        	}
        	if (update[4]) subCam.theta=parVect[4];              // 4 degrees, optical axis from the eyesis horizon, positive - up
        	if (update[5]) subCam.psi=parVect[5];                // 5 degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
        	if (update[6]) this.goniometerHorizontal[stationNumber]=parVect[6]; // 6 goniometer rotation around "horizontal" axis (tilting from the target - positive)
        	if (update[7]) this.goniometerAxial[stationNumber]=parVect[7];      // 7 goniometer rotation around Eyesis axis (clockwise in plan - positive
        	if (update[8]) this.interAxisDistance[stationNumber]=parVect[8];    // 8 distance in mm between two goniometer axes
        	if (update[9]) this.interAxisAngle[stationNumber]=parVect[9];       // 9 angle in degrees between two goniometer axes minus 90. negative if "vertical" axis is rotated
        	if (update[10]) this.horAxisErrPhi[stationNumber]=parVect[10];       //10 angle in degrees "horizontal" goniometer axis is rotated around target Y axis from target X axis (CW)
        	if (update[11]) this.horAxisErrPsi[stationNumber]=parVect[11];       //11 angle in degrees "horizontal" goniometer axis is rotated around moving X axis (up)
        	if (update[12]) this.entrancePupilForward[stationNumber]=parVect[12];             //12 coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
        	if (update[13]) this.centerAboveHorizontal[stationNumber]=parVect[13];             //13  y
        	if (update[14]) this.GXYZ[stationNumber][0]=parVect[14];             //14 coordinates (in mm) of the goniometer horizontal axis closest to the moving one in target system
        	if (update[15]) this.GXYZ[stationNumber][1]=parVect[15];             //15  y
        	if (update[16]) this.GXYZ[stationNumber][2]=parVect[16];             //16  z
        	if (update[17]) subCam.focalLength=parVect[17];       //17  lens focal length
        	if (update[18]) subCam.px0=parVect[18];               //18 center of the lens on the sensor, pixels
        	if (update[19]) subCam.py0=parVect[19];               //19 center of the lens on the sensor, pixels
        	if (update[20]) subCam.distortionA8=parVect[20];      //20  r^8 (normalized to focal length or to sensor half width?)
        	if (update[21]) subCam.distortionA7=parVect[21];      //21  r^7 (normalized to focal length or to sensor half width?)
        	if (update[22]) subCam.distortionA6=parVect[22];      //22  r^6 (normalized to focal length or to sensor half width?)
        	if (update[23]) subCam.distortionA5=parVect[23];      //23  r^5 (normalized to focal length or to sensor half width?)
        	if (update[24]) subCam.distortionA= parVect[24];      //24  r^4 (normalized to focal length or to sensor half width?)
        	if (update[25]) subCam.distortionB= parVect[25];      //25  r^3
        	if (update[26]) subCam.distortionC= parVect[26];      //26  r^2
        	if (update[27]) subCam.r_od[0][0]= parVect[27];
        	if (update[28]) subCam.r_od[0][1]= parVect[28];
        	if (update[29]) subCam.r_xy[0][0]= parVect[29];
        	if (update[30]) subCam.r_xy[0][1]= parVect[30];
        	if (update[31]) subCam.r_od[1][0]= parVect[31];
        	if (update[32]) subCam.r_od[1][1]= parVect[32];
        	if (update[33]) subCam.r_xy[1][0]= parVect[33];
        	if (update[34]) subCam.r_xy[1][1]= parVect[34];
        	if (update[35]) subCam.r_od[2][0]= parVect[35];
        	if (update[36]) subCam.r_od[2][1]= parVect[36];
        	if (update[37]) subCam.r_xy[2][0]= parVect[37];
        	if (update[38]) subCam.r_xy[2][1]= parVect[38];
        	if (update[39]) subCam.r_od[3][0]= parVect[39];
        	if (update[40]) subCam.r_od[3][1]= parVect[40];
        	if (update[41]) subCam.r_xy[3][0]= parVect[41];
        	if (update[42]) subCam.r_xy[3][1]= parVect[42];
        	if (update[43]) subCam.r_od[4][0]= parVect[43];
        	if (update[44]) subCam.r_od[4][1]= parVect[44];
        	if (update[45]) subCam.r_xy[4][0]= parVect[45];
        	if (update[46]) subCam.r_xy[4][1]= parVect[46];
        	if (update[47]) subCam.r_od[5][0]= parVect[47];
        	if (update[48]) subCam.r_od[5][1]= parVect[48];
        	if (update[49]) subCam.r_xy[5][0]= parVect[49];
        	if (update[50]) subCam.r_xy[5][1]= parVect[50];
        	if (update[51]) subCam.r_od[6][0]= parVect[51];
        	if (update[52]) subCam.r_od[6][1]= parVect[52];
        }

        public void updateCartesian(){
        	for (int numStation = 0; numStation < this.eyesisSubCameras.length; numStation++){
        		for (int numSub = 0; numSub < this.eyesisSubCameras[numStation].length; numSub++){
        			this.eyesisSubCameras[numStation][numSub].setCartesian(this.cartesian);
        		}
        	}
        }

    	public void initSubCameras(
    			int numStation,
    			int numSubCameras){
    		System.out.println("initSubCameras("+numStation+","+numSubCameras+")");
    		if ((this.eyesisSubCameras == null) || (this.eyesisSubCameras.length != this.numStations)) {
    			this.eyesisSubCameras = new EyesisSubCameraParameters[this.numStations][];
    		}
    		this.eyesisSubCameras[numStation]=new EyesisSubCameraParameters[numSubCameras];
    		for (int i=0;i<numSubCameras;i++)  this.eyesisSubCameras[numStation][i]=null;
    		if (numSubCameras==3) {
    			this.cartesian = false; // change?
    			this.eyesisSubCameras[numStation][0]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					0.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					52.53, // double radius,  // mm, distance from the rotation axis
    					34.64, // double height,  // mm, up (was downwards) - from the origin point
    					0.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					180.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel


    			this.eyesisSubCameras[numStation][1]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					30.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					60.0, // double radius,  // mm, distance from the rotation axis
    					-17.32, // double height,  // mm, up (was downwards) - from the origin point
    					-30.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					180.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][2]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-30.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					60.0, // double radius,  // mm, distance from the rotation axis
    					-17.32, // double height,  // mm, up (was downwards) - from the origin point
    					30.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					180.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    		} else if (numSubCameras==1) {
    			this.cartesian = false;
    			this.eyesisSubCameras[numStation][0]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					0.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					0.0, // double radius,  // mm, distance from the rotation axis
    					0.0, // double height,  // mm, up (was downwards) - from the origin point
    					0.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					0.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    		} else if (numSubCameras == 21) {
    			// ================
    			// PHG21 parameters
    			//
    			this.cartesian = false; // change
    			this.eyesisSubCameras[numStation][0]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					0.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					46.57, // double radius,  // mm, distance from the rotation axis
    					0.0, // double height,  // mm, up (was downwards) - from the origin point
    					0.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][1]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					21.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					50.36, // double radius,  // mm, distance from the rotation axis
    					-15.0, // double height,  // mm, up (was downwards) - from the origin point
    					-53.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][2]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-21.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					50.36, // double radius,  // mm, distance from the rotation axis
    					-15.0, // double height,  // mm, up (was downwards) - from the origin point
    					53.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][3]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					0.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					46.57, // double radius,  // mm, distance from the rotation axis
    					70.0, // double height,  // mm, up (was downwards) - from the origin point
    					0.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][4]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					21.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					50.36, // double radius,  // mm, distance from the rotation axis
    					55.0, // double height,  // mm, up (was downwards) - from the origin point
    					-53.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][5]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-21.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					50.36, // double radius,  // mm, distance from the rotation axis
    					55.0, // double height,  // mm, up (was downwards) - from the origin point
    					53.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][6]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					52.47, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					76.45, // double radius,  // mm, distance from the rotation axis
    					35.0, // double height,  // mm, up (was downwards) - from the origin point
    					-52.47, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][7]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					59.13, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					91.65, // double radius,  // mm, distance from the rotation axis
    					20.0, // double height,  // mm, up (was downwards) - from the origin point
    					-91.13, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][8]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					42.16, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					63.43, // double radius,  // mm, distance from the rotation axis
    					20.0, // double height,  // mm, up (was downwards) - from the origin point
    					-10.16, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][9]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					52.47, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					76.45, // double radius,  // mm, distance from the rotation axis
    					-35.0, // double height,  // mm, up (was downwards) - from the origin point
    					-52.47, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][10]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					59.13, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					91.65, // double radius,  // mm, distance from the rotation axis
    					-50.0, // double height,  // mm, up (was downwards) - from the origin point
    					-91.13, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][11]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					42.16, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					63.43, // double radius,  // mm, distance from the rotation axis
    					-50.0, // double height,  // mm, up (was downwards) - from the origin point
    					-10.16, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][12]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					0.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					46.57, // double radius,  // mm, distance from the rotation axis
    					-70.0, // double height,  // mm, up (was downwards) - from the origin point
    					0.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][13]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					21.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					50.36, // double radius,  // mm, distance from the rotation axis
    					-85.0, // double height,  // mm, up (was downwards) - from the origin point
    					-53.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][14]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-21.0, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					50.36, // double radius,  // mm, distance from the rotation axis
    					-85.0, // double height,  // mm, up (was downwards) - from the origin point
    					53.0, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][15]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-52.47, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					76.45, // double radius,  // mm, distance from the rotation axis
    					-35.0, // double height,  // mm, up (was downwards) - from the origin point
    					52.47, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][16]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-42.16, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					63.43, // double radius,  // mm, distance from the rotation axis
    					-50.0, // double height,  // mm, up (was downwards) - from the origin point
    					10.16, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][17]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-59.13, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					91.65, // double radius,  // mm, distance from the rotation axis
    					-50.0, // double height,  // mm, up (was downwards) - from the origin point
    					91.13, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][18]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-52.47, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					76.45, // double radius,  // mm, distance from the rotation axis
    					35.0, // double height,  // mm, up (was downwards) - from the origin point
    					52.47, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][19]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-42.16, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					63.43, // double radius,  // mm, distance from the rotation axis
    					20.0, // double height,  // mm, up (was downwards) - from the origin point
    					10.16, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			this.eyesisSubCameras[numStation][20]=new EyesisSubCameraParameters( //TODO:  modify for lens adjustment defaults?
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					-59.13, // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					91.65, // double radius,  // mm, distance from the rotation axis
    					20.0, // double height,  // mm, up (was downwards) - from the origin point
    					91.13, // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0, //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		5.4, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			//
    			// end of PHG21 parameters
    			// =======================
    			} else {
    			// default setup for the 26 sub-cameras
    			for (int i=0;i<8;i++) if (i<numSubCameras) 	this.eyesisSubCameras[numStation][i]=new EyesisSubCameraParameters( // top 8 cameras
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					45.0*i,      // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					41.540,      // double radius,  // mm, distance from the rotation axis
    					42.883,      // double height,  // mm, up (was downwards?) - from the origin point
    					0.0,         // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					60.0,        //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			for (int i=8;i<16;i++) if (i<numSubCameras) 	this.eyesisSubCameras[numStation][i]=new EyesisSubCameraParameters( // middle 8 cameras
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					45.0*(i-8),  // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					54.525,      // double radius,  // mm, distance from the rotation axis
    					0.0, // double height,  // mm, up (was downwards) - from the origin point
    					0.0,         // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0,         //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			for (int i=16;i<24;i++) if (i<numSubCameras) 	this.eyesisSubCameras[numStation][i]=new EyesisSubCameraParameters( // bottom eight cameras
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					true,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					45.0*(i-16), // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					41.540,      // double radius,  // mm, distance from the rotation axis
    					-42.883,     // double height,  // mm, up (was downwards?) - from the origin point
    					0.0,         // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					-60.0,       //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					1.0, //channelWeightDefault
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			if (24<numSubCameras) 	this.eyesisSubCameras[numStation][24]=new EyesisSubCameraParameters(
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					false,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					90,      // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					12.025,  // double radius,  // mm, distance from the rotation axis
    					-807.0,  // double height,  // mm, up - from the origin point
    					0.0,     // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0,     //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					90.0,  //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5, // double focalLength
    		    		2.2, // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					8.0, //channelWeightDefault  (was 4)
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    			if (25<numSubCameras) 	this.eyesisSubCameras[numStation][25]=new EyesisSubCameraParameters(
    					this.defaultSensorWidth,
    					this.defaultSensorHeight,
    					this.defaultDecimateMasks,
    					this.cartesian,
    					defaultLensDistortionModel,
    					false,
    					0.0, 0.0, 0.0, // cartesian righ/forward/heading
    					270,     // double azimuth, // azimuth of the lens entrance pupil center, degrees, clockwise looking from top
    					12.025,  // double radius,  // mm, distance from the rotation axis
    					-841.0,  // double height,  // mm, up - from the origin point
    					0.0,     // double phi,     // degrees, optical axis from azimuth/r vector, clockwise
    					0.0,     //double theta,   // degrees, optical axis from the eyesis horizon, positive - up
    					-90.0,    //double psi;      // degrees, rotation (of the sensor) around the optical axis. Positive if camera is rotated clockwise looking to the target
    		    		4.5,    // double focalLength
    		    		2.2,    // double pixelSize (um)
    		    		2.8512, //double distortionRadius mm - half width of the sensor
    		    		0.0, // double distortionA8 // r^8 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA7 // r^7 (normalized to focal length or to sensor half width?)
    		    		0.0, // double distortionA6 // r^6 (normalized to focal length or to sensor half width?)
    		    		0.0,    // double distortionA5 // r^5 (normalized to focal length or to sensor half width?)
    		    		0.0,    // double distortionA // r^4 (normalized to focal length or to sensor half width?)
    		    		0.0,    // double distortionB // r^3
    		    		0.0,    // double distortionC // r^2
    		    		1296.0, // double px0=1296.0;          // center of the lens on the sensor, pixels
    					968.0, // double py0=968.0;           // center of the lens on the sensor, pixels
    					null,  // eccentricity for b,a,a5,a6,a7,a8
    					null,  // elongation for c,b,a,a5,a6,a7,a8
    					8.0, //channelWeightDefault  (was 4)
    					0,   // public int subcamera
    					0,   // public int sensor_port
    					0);   // public int subchannel

    		}
    	}
    	public void recenterVertically(boolean [] subcams, boolean [] stations){
    		boolean sameCenterAboveHorizontal=true;
    		double cah=Double.NaN;
    		for (int i=0;i<stations.length;i++){
    			if (stations[i]) {
    				if (Double.isNaN(cah)) cah=this.centerAboveHorizontal[i];
    				else if (cah!=this.centerAboveHorizontal[i]) sameCenterAboveHorizontal=false;
    			}
    		}
    		if (Double.isNaN(cah)){
    			System.out.println("No stations enabled, nothing to do for vertical centering");
    			return;
    		}
    		System.out.println("centerAboveHorizontal "+
    		(sameCenterAboveHorizontal?"is common for all stations":"differs between stations"));

    		if (sameCenterAboveHorizontal){
    	   		System.out.println("Re-centering vertically for centerAboveHorizontal common for all stations ");

    			double sumWeightedHeights=0.0;
    			double sumWeights=0.0;

    			for (int i=0;i<stations.length;i++) if (stations[i]) {
    				for (int subIndex=0; subIndex<subcams.length;subIndex++) if (subcams[subIndex]){
    					System.out.println("Averaging station "+i+", subcamera "+subIndex);
    					sumWeights+=stationWeight[i];
    					sumWeightedHeights+=stationWeight[i]*eyesisSubCameras[i][subIndex].height;
    				}
    			}
        		if (sumWeights==0.0){
        			System.out.println("No subcams are enabled, nothing to do for vertical centering");
        			return;
        		}
    			sumWeightedHeights/=sumWeights;
    			for (int i=0;i<stations.length;i++) if (stations[i]) {
    				for (int subIndex=0; subIndex<subcams.length;subIndex++) { // need to update all channels, not only averaged
    					eyesisSubCameras[i][subIndex].height-=sumWeightedHeights;
    				}
					this.centerAboveHorizontal[i]+=sumWeightedHeights;
    			}
    		} else {
    	   		System.out.println("Re-centering vertically for centerAboveHorizontal individual for each station");
    			for (int i=0;i<stations.length;i++) if (stations[i]) {
        			double sumWeightedHeights=0.0;
        			double sumWeights=0.0;
    				for (int subIndex=0; subIndex<subcams.length;subIndex++) if (subcams[subIndex]){
    					System.out.println("Averaging station "+i+", subcamera "+subIndex);
    					sumWeights+=stationWeight[i];
    					sumWeightedHeights+=stationWeight[i]*eyesisSubCameras[i][subIndex].height;
    				}
            		if (sumWeights==0.0){
            			System.out.println("No subcams are enabled, nothing to do for vertical centering");
            			return;
            		}
        			sumWeightedHeights/=sumWeights;
    				for (int subIndex=0; subIndex<subcams.length;subIndex++) {  // need to update all channels, not only averaged
    					eyesisSubCameras[i][subIndex].height-=sumWeightedHeights;
    				}
					this.centerAboveHorizontal[i]+=sumWeightedHeights;
    			}

    		}
    	}

    	public class SubsystemOffsets {
    		public boolean use_first_station = true; // import data from first station, duplicate to all
    		public int     offset_channel =    4; // 0;
    		public int     new_subcam =        1; // new subcamera number
    		public double  offset_height =    50.8;
    		public double  offset_right =    -35.0; //eo -35., lwir +35.0
    		public double  offset_forward =   15.0; //eo +??,  lwir -??
    		public double  offset_heading =    0.0;
    		public double  offset_elevation =  0.0;
    		public double  offset_roll =       0.0;
    		public boolean update_sensor_files = true; // Update an re-write sensor files
    		public void dialogQuestions(GenericJTabbedDialog gd) {
    			gd.addCheckbox    ("Use first station", this.use_first_station, "Import data for the first station only, clone for all current ones");
    			gd.addNumericField("Channel offset",    this.offset_channel,   0,3,"","Add this to the sensor number after impofrt");
    			gd.addNumericField("New subcamera",     this.new_subcam,        0,3,"","New subcamera number ater import");
    			gd.addNumericField("Offset height",     this.offset_height,    3,6,"mm", "Place imported sub camera above composite camera center");
    			gd.addNumericField("Offset right",      this.offset_right,     3,6,"mm", "Visible range camera exposure time (all channels)");
    			gd.addNumericField("Offset forward",    this.offset_forward,   3,6,"mm", "Visible range camera exposure time (all channels)");
    			gd.addNumericField("Offset heading",    this.offset_heading,   3,6,"°", "Visible range camera exposure time (all channels)");
    			gd.addNumericField("Offset elevation",  this.offset_elevation, 3,6,"°", "Visible range camera exposure time (all channels)");
    			gd.addNumericField("Offset roll",       this.offset_roll,      3,6,"°", "Visible range camera exposure time (all channels)");
    			gd.addCheckbox    ("Transform sensor files", this.update_sensor_files, "Import sesnor files (ifavailable), re-write them for new extrinsics/channels");
    		}
    		public void dialogAnswers(GenericJTabbedDialog gd) {
    			this.use_first_station =      gd.getNextBoolean();
    			this.offset_channel =   (int) gd.getNextNumber();
    			this.new_subcam =       (int) gd.getNextNumber();
    			this.offset_height =          gd.getNextNumber();
    			this.offset_right =           gd.getNextNumber();
    			this.offset_forward =         gd.getNextNumber();
    			this.offset_heading =         gd.getNextNumber();
    			this.offset_elevation =       gd.getNextNumber();
    			this.offset_roll =            gd.getNextNumber();
    			this.update_sensor_files =    gd.getNextBoolean();
    		}
    		public boolean dialogOffests() {
    				GenericJTabbedDialog gd = new GenericJTabbedDialog("Set camera subsystem offsets",250,500);
    				dialogQuestions(gd);
    				gd.showDialog();
    				if (gd.wasCanceled()) return false;
    				dialogAnswers(gd);
    			return true;
    		}

    	}
    	public EyesisSubCameraParameters offsetSubCamera(SubsystemOffsets offsets, EyesisSubCameraParameters sub_camera) {
    		sub_camera.updateCartesian();
    		EyesisSubCameraParameters new_sub = sub_camera.clone();
        	double psi=  Math.PI/180*offsets.offset_roll;      // radians
        	double theta=Math.PI/180*offsets.offset_elevation; // radians
        	double phi=  Math.PI/180*offsets.offset_heading;   // radians

        	double psi_s=  Math.PI/180*sub_camera.heading;      // radians
        	double theta_s=Math.PI/180*sub_camera.theta;        // radians
        	double phi_s=  Math.PI/180*sub_camera.psi;          // radians


        	double [][] asub_c = {{sub_camera.right},{sub_camera.height},{sub_camera.forward}}; // subcamera center
        	// rotate by -psi around Z
         	double [][] aRz={
         			{ Math.cos(psi),Math.sin(psi), 0.0},
         			{-Math.sin(psi),Math.cos(psi), 0.0},
         			{ 0.0,          0.0,           1.0}};
        	// rotate by -psi around x
         	double [][] aRx={
         			{1.0,  0.0,            0.0},
         			{0.0, Math.cos(theta),Math.sin(theta)},
         			{0.0,-Math.sin(theta),Math.cos(theta)}};
        	// rotate by -phi around y
         	double [][] aRy={
         			{Math.cos(phi),  0.0,  Math.sin(phi)},
         			{0.0,1.0,0.0},
         			{-Math.sin(phi), 0.0,  Math.cos(phi)}};
         	double [][] aoffs = {{offsets.offset_right},{offsets.offset_height},{offsets.offset_forward}};

        	// rotate by -psi around Z
         	double [][] aRz_s={
         			{ Math.cos(psi_s),Math.sin(psi_s), 0.0},
         			{-Math.sin(psi_s),Math.cos(psi_s), 0.0},
         			{ 0.0,          0.0,           1.0}};
        	// rotate by -psi around x
         	double [][] aRx_s={
         			{1.0,  0.0,            0.0},
         			{0.0, Math.cos(theta_s),Math.sin(theta_s)},
         			{0.0,-Math.sin(theta_s),Math.cos(theta_s)}};
        	// rotate by -phi around y
         	double [][] aRy_s={
         			{Math.cos(phi_s),  0.0,  Math.sin(phi_s)},
         			{0.0,1.0,0.0},
         			{-Math.sin(phi_s), 0.0,  Math.cos(phi_s)}};


        	Matrix sub_c=new Matrix(asub_c);
        	Matrix Rz=   new Matrix(aRz);
        	Matrix Rx=   new Matrix(aRx);
        	Matrix Ry=   new Matrix(aRy);
        	Matrix offs= new Matrix(aoffs);
        	Matrix R = Ry.times(Rx.times(Rz));
        	Matrix Rz_s=   new Matrix(aRz_s);
        	Matrix Rx_s=   new Matrix(aRx_s);
        	Matrix Ry_s=   new Matrix(aRy_s);
        	Matrix R_s = Ry_s.times(Rx_s.times(Rz_s));

         	double [] rhf = offs.plus(R.times(sub_c)).getRowPackedCopy();
         	/*
         	System.out.println("R=");
         	R.print(8, 3);
         	System.out.println("\nR_s=");
         	R_s.print(8, 3);
         	*/
         	Matrix R_t = R.times(R_s);
         	/*
         	System.out.println("\nR_t=");
         	R_t.print(8, 3);
         	*/

         	new_sub.cartesian = true;
         	new_sub.right =   rhf[0];
         	new_sub.height =  rhf[1];
         	new_sub.forward = rhf[2];

         	// projection of camera axis (z) on XZ plane
         	double min_cos = 0.001;
         	double zXZ = Math.sqrt(R_t.get(0, 2)*R_t.get(0, 2) + R_t.get(2, 2)*R_t.get(2, 2));
         	if (zXZ < min_cos) {
         		System.out.append("zXZ = "+zXZ);
         	}
         	double new_phi =   Math.atan2(R_t.get(0, 2), R_t.get(2, 2));
         	double new_theta = Math.atan2(R_t.get(1, 2), zXZ);

         	double new_psi = Math.atan2(R_t.get(0, 1), R_t.get(1, 1));

         	new_sub.heading = 180.0/Math.PI*new_phi;
         	new_sub.theta =   180.0/Math.PI*new_theta;
         	new_sub.psi =     180.0/Math.PI*new_psi;

    		new_sub.updateCartesian();
    		return new_sub;
    	}

    	public boolean importSystem(
    			Properties properties,
    			String prefix,
    			SubsystemOffsets subsystemOffsets,
    			Distortions systemDistortions,
    			DistortionCalibrationData system_distortionCalibrationData,
    			String calibration_directory) {
    		EyesisCameraParameters sub_system = new EyesisCameraParameters();
    		sub_system.getProperties(prefix, properties);
    		if (subsystemOffsets == null) {
    			subsystemOffsets = new SubsystemOffsets();
    		}
    		if (!subsystemOffsets.dialogOffests()) {
    			return false;
    		}
    		System.out.println(
    				"use_first_stationl=" + subsystemOffsets.use_first_station +
    				"offset_channel="     + subsystemOffsets.offset_channel +
    				"\noffset_height="    + subsystemOffsets.offset_height +
    				"\noffset_right="     + subsystemOffsets.offset_right +
    				"\noffset_forward="   + subsystemOffsets.offset_forward +
    				"\noffset_heading="   + subsystemOffsets.offset_heading +
    				"\noffset_elevation=" + subsystemOffsets.offset_elevation +
    				"\noffset_roll="      + subsystemOffsets.offset_roll);

    		// remove all but first imported station if instructed so
    		if (subsystemOffsets.use_first_station) {
    			sub_system.updateNumstations (1);
    		}
    		// Import sensor files
    		String sensors_path = null;
    		Distortions sub_distortions = null;
    		DistortionCalibrationData sub_distortionCalibrationData = null;
    		EyesisAberrations.AberrationParameters sub_aberrationParameters = null;
    		LensDistortionParameters sub_lensDistortionParameters = null;
    		// TODO: check sub_distortions != null
    		if (subsystemOffsets.update_sensor_files) {
    			sub_aberrationParameters=new EyesisAberrations.AberrationParameters();
    			sub_aberrationParameters.getProperties("ABERRATIONS_PARAMETERS.", properties);
    			sub_lensDistortionParameters = new LensDistortionParameters();
    			sub_lensDistortionParameters.getProperties("LENS_DISTORTION_PARAMETERS.", properties);

    			sensors_path =  sub_aberrationParameters.sensorsPath;
    			System.out.println("sensors_path = "+sensors_path);
///    			String [][] stationFilenames = new String[sub_system.numStations][0];
    			sub_distortionCalibrationData = new DistortionCalibrationData(
    	        		sub_system // EyesisCameraParameters eyesisCameraParameters
    					);
    			// now read all sensor files
    			if ((sensors_path !=null) && (sensors_path != "")){ // load sensor
    				sub_distortions = new Distortions(
    						sub_lensDistortionParameters, // LensDistortionParameters lensDistortionParameters,
    						null, // PatternParameters patternParameters,
    						null, // RefineParameters refineParameters,
    						null //AtomicInteger stopRequested
    						);

    				// sub_distortions.lensDistortionParameters is null!
    				sub_distortions.setDistortionFromImageStack(
    						sub_distortionCalibrationData,
    						sub_system, // EYESIS_CAMERA_PARAMETERS, // use null for old version - in fitting strategy instance
    						sensors_path,
    						sub_aberrationParameters.autoRestoreSensorOverwriteOrientation,
    						sub_aberrationParameters.autoRestoreSensorOverwriteDistortion
    						);
    			} else {
    				System.out.println("Sensor files are not specified");
//    				update_sensor_files = false;
    			}



    		}



    		// TODO:recalculate extrinsics before import
    		for (int ns = 0; ns < sub_system.eyesisSubCameras.length; ns++) {
        		for (int nc = 0; nc < sub_system.eyesisSubCameras[ns].length; nc++) {
        			sub_system.eyesisSubCameras[ns][nc] = offsetSubCamera(subsystemOffsets,sub_system.eyesisSubCameras[ns][nc]);
        			sub_system.eyesisSubCameras[ns][nc].subcamera = subsystemOffsets.new_subcam;
        		}
    		}


    		// increase number of stations if needed to match existing
    		if (this.getNumStations() > sub_system.getNumStations()){
    			 sub_system.updateNumstations (this.getNumStations());
    		}

    		// increase number of this stations if needed
    		if (!subsystemOffsets.use_first_station && (this.getNumStations() < sub_system.getNumStations())){
    			updateNumstations (sub_system.getNumStations());
    		}

    		for (int numStation = 0; numStation < this.getNumStations(); numStation++) {
        		// increase number of cameras if needed to accommodate import
    			if (this.getNumChannels(numStation) < (sub_system.getNumChannels(numStation) + subsystemOffsets.offset_channel)) {
    				EyesisSubCameraParameters [] stationCameras = eyesisSubCameras[numStation];
    				int new_channels = sub_system.getNumChannels(numStation) + subsystemOffsets.offset_channel;
    				eyesisSubCameras[numStation] = new EyesisSubCameraParameters[new_channels];
    				for (int nc = 0; nc <stationCameras.length; nc++) {
    					eyesisSubCameras[numStation][nc] = stationCameras[nc];
    				}
    			}
				for (int nc = 0; nc < sub_system.getNumChannels(); nc++) {
					eyesisSubCameras[numStation][nc+subsystemOffsets.offset_channel] = sub_system.eyesisSubCameras[numStation][nc];
				}
    		}
    		if (sub_distortions !=null) {
    			boolean saveNonCalibrated= true;
    			int new_channels = sub_system.getNumChannels() + subsystemOffsets.offset_channel;
    			// expand arrays if needed
    			if (systemDistortions.pathNames == null) {
    				systemDistortions.pathNames = new String[new_channels];
    			} else 	if (systemDistortions.pathNames.length < new_channels) {
    				String [] tmpNames = systemDistortions.pathNames;
    				systemDistortions.pathNames = new String[new_channels];
    				for (int i = 0; i < tmpNames.length; i++ ) systemDistortions.pathNames[i] = tmpNames[i];
    			}
    			if (systemDistortions.pixelCorrection == null) {
    				systemDistortions.pixelCorrection = new double[new_channels][][];
    			} else	if (systemDistortions.pixelCorrection.length < new_channels) {
    				double [][][] tmpPixelCorrection = systemDistortions.pixelCorrection;
    				systemDistortions.pixelCorrection = new double[new_channels][][];
    				for (int i = 0; i < tmpPixelCorrection.length; i++ ) systemDistortions.pixelCorrection[i] = tmpPixelCorrection[i];
    			}
    			if (system_distortionCalibrationData == null) {
    				system_distortionCalibrationData = new DistortionCalibrationData (this);
    			}
    			if (system_distortionCalibrationData.sensorMasks == null) {
    				system_distortionCalibrationData.sensorMasks = new double [new_channels][];
    			} else if (system_distortionCalibrationData.sensorMasks.length < new_channels) {
    				double [][] tmp_masks = system_distortionCalibrationData.sensorMasks;
    				system_distortionCalibrationData.sensorMasks = new double [new_channels][];
    				for (int i = 0; i < tmp_masks.length; i++ ) system_distortionCalibrationData.sensorMasks[i] = tmp_masks[i];
    			}
    			// now copy data over, update the path names?
///    			String imported_name=sub_distortions.pathNames[0];
///    			int last_sep =  imported_name.lastIndexOf(Prefs.getFileSeparator());
///    			if (last_sep>=0) imported_name = imported_name.substring(last_sep + 1);
///    			int indexPeriod=imported_name.lastIndexOf('.');
///    			int indexSuffix=indexPeriod;
///    			String digits="0123456789";
///    	    	for (int i=1;i<=2;i++) if (digits.indexOf(imported_name.charAt(indexSuffix-1))>=0) indexSuffix--; // remove 1 or 2 digits before period
///    	    	boolean hadSuffix= (imported_name.charAt(indexSuffix-1)=='-');
    	    	for (int nc = 0; nc < sub_system.getNumChannels(); nc++) {
    	    		int chn = nc+subsystemOffsets.offset_channel;
//    	    		systemDistortions.pathNames[chn]=calibration_directory+Prefs.getFileSeparator()+((hadSuffix?imported_name.substring(0,indexSuffix):(imported_name.substring(0,indexPeriod)+"-"))+
//    	    	    		String.format("%02d",chn)+imported_name.substring(indexPeriod));
    	    		systemDistortions.pixelCorrection[chn] = sub_distortions.pixelCorrection[nc];
    	    		system_distortionCalibrationData.sensorMasks[chn] = sub_distortionCalibrationData.sensorMasks[nc];
    	    	}
    			// systemDistortions.pathnames
    			// systemDistortions.pixelCorrection
    			// sub_distortionCalibrationData.sensorMasks

    	    	String [] extensions={".calib-tiff"};
    	    	CalibrationFileManagement.MultipleExtensionsFileFilter parFilter = new CalibrationFileManagement.MultipleExtensionsFileFilter("",extensions,"distortion calibration .calib-tiff files");
    	    	String pathname=CalibrationFileManagement.selectFile(true,
    	    			"Save distortion calibration for sensor (will add channel number when saving all)",
    	    			"Save",
    	    			parFilter,
    	    			systemDistortions.getSensorPath(-1)); //String defaultPath
    	    	if ((pathname==null) || (pathname=="")) return true;
///    			int last_sep =  pathname.lastIndexOf(Prefs.getFileSeparator());
///    			if (last_sep>=0) pathname = pathname.substring(last_sep + 1);
    			int indexPeriod=pathname.lastIndexOf('.');
    			int indexSuffix=indexPeriod;
    			String digits="0123456789";
    	    	for (int i=1;i<=2;i++) if (digits.indexOf(pathname.charAt(indexSuffix-1))>=0) indexSuffix--; // remove 1 or 2 digits before period
    	    	boolean hadSuffix= (pathname.charAt(indexSuffix-1)=='-');


    	    	for (int nc = 0; nc < sub_system.getNumChannels(); nc++) {
    	    		int chn = nc+subsystemOffsets.offset_channel;
    	    		// Make systemDistortions.pathNames[chn] from pathname replacing channel
    	    		systemDistortions.pathNames[chn]= // calibration_directory+Prefs.getFileSeparator()+
    	    				((hadSuffix?pathname.substring(0,indexSuffix):(pathname.substring(0,indexPeriod)+"-"))+
    	    				String.format("%02d",chn)+pathname.substring(indexPeriod));

    	    		systemDistortions.saveDistortionAsImageStack(
    	    				system_distortionCalibrationData,
    	    				null, // camerasInterface, // to save channel map
    	    				"sensor_calibration", // title,
    	    				systemDistortions.pathNames[chn], // channelPath,
    	    				chn,
    	    				saveNonCalibrated);

    	    	}


    		}


    		return true;
    	}
    }