head	8.7;
access;
symbols;
locks; strict;
comment	@ * @;


8.7
date	2026.05.11.12.01.10;	author mcalabre;	state Exp;
branches;
next	8.6;

8.6
date	2026.03.29.13.53.56;	author mcalabre;	state Exp;
branches
	8.6.1.1;
next	8.5;

8.5
date	2025.12.06.13.47.41;	author mcalabre;	state Exp;
branches
	8.5.1.1;
next	8.4;

8.4
date	2024.10.28.13.56.16;	author mcalabre;	state Exp;
branches
	8.4.1.1;
next	8.3;

8.3
date	2024.05.13.16.33.00;	author mcalabre;	state Exp;
branches
	8.3.1.1;
next	8.2;

8.2
date	2023.11.16.10.00.29;	author mcalabre;	state Exp;
branches
	8.2.1.1;
next	8.1;

8.1
date	2023.07.05.17.12.07;	author mcalabre;	state Exp;
branches
	8.1.1.1;
next	8.0;

8.0
date	2023.07.01.06.15.04;	author mcalabre;	state Exp;
branches
	8.0.1.1;
next	7.13;

7.13
date	2022.10.07.03.43.35;	author mcalabre;	state Exp;
branches
	7.13.1.1;
next	7.12;

7.12
date	2022.09.09.04.57.58;	author mcalabre;	state Exp;
branches
	7.12.1.1;
next	7.11;

7.11
date	2022.04.26.06.13.52;	author mcalabre;	state Exp;
branches
	7.11.1.1;
next	7.10;

7.10
date	2022.04.23.14.25.15;	author mcalabre;	state Exp;
branches
	7.10.1.1;
next	7.9;

7.9
date	2022.03.25.15.14.48;	author mcalabre;	state Exp;
branches
	7.9.1.1;
next	7.8;

7.8
date	2022.03.25.06.11.49;	author mcalabre;	state Exp;
branches
	7.8.1.1;
next	7.7;

7.7
date	2021.07.12.06.36.49;	author mcalabre;	state Exp;
branches
	7.7.1.1;
next	7.6;

7.6
date	2021.04.13.12.57.01;	author mcalabre;	state Exp;
branches
	7.6.1.1;
next	7.5;

7.5
date	2021.03.20.05.54.58;	author mcalabre;	state Exp;
branches
	7.5.1.1;
next	7.4;

7.4
date	2021.01.31.02.24.51;	author mcalabre;	state Exp;
branches
	7.4.1.1;
next	7.3;

7.3
date	2020.06.03.03.37.02;	author mcalabre;	state Exp;
branches
	7.3.1.1;
next	7.2;

7.2
date	2020.03.09.07.31.23;	author mcalabre;	state Exp;
branches
	7.2.1.1;
next	7.1;

7.1
date	2019.12.31.13.25.19;	author mcalabre;	state Exp;
branches
	7.1.1.1;
next	6.4;

6.4
date	2019.08.15.09.30.18;	author mcalabre;	state Exp;
branches
	6.4.1.1;
next	6.3;

6.3
date	2019.07.12.07.33.39;	author mcalabre;	state Exp;
branches
	6.3.1.1;
next	6.2;

6.2
date	2018.10.20.10.03.13;	author mcalabre;	state Exp;
branches
	6.2.1.1;
next	6.1;

6.1
date	2018.10.19.08.35.17;	author mcalabre;	state Exp;
branches
	6.1.1.1;
next	5.20;

5.20
date	2018.10.05.05.32.41;	author mcalabre;	state Exp;
branches
	5.20.1.1;
next	5.19;

5.19
date	2018.07.26.15.37.04;	author mcalabre;	state Exp;
branches
	5.19.1.1;
next	5.18;

5.18
date	2018.01.10.08.32.14;	author mcalabre;	state Exp;
branches
	5.18.1.1;
next	5.17;

5.17
date	2017.09.18.08.44.23;	author mcalabre;	state Exp;
branches
	5.17.1.1;
next	5.16;

5.16
date	2017.01.15.04.25.01;	author mcalabre;	state Exp;
branches
	5.16.1.1;
next	5.15;

5.15
date	2016.04.05.12.55.10;	author mcalabre;	state Exp;
branches
	5.15.1.1;
next	5.14;

5.14
date	2016.02.07.10.49.31;	author mcalabre;	state Exp;
branches
	5.14.1.1;
next	5.13;

5.13
date	2016.01.26.08.51.13;	author mcalabre;	state Exp;
branches
	5.13.1.1;
next	5.12;

5.12
date	2015.11.15.02.13.00;	author mcalabre;	state Exp;
branches
	5.12.1.1;
next	5.11;

5.11
date	2015.10.18.09.13.05;	author mcalabre;	state Exp;
branches
	5.11.1.1;
next	5.10;

5.10
date	2015.10.09.08.19.15;	author mcalabre;	state Exp;
branches
	5.10.1.1;
next	5.9;

5.9
date	2015.07.21.09.20.01;	author mcalabre;	state Exp;
branches
	5.9.1.1;
next	5.8;

5.8
date	2015.07.08.11.03.59;	author mcalabre;	state Exp;
branches
	5.8.1.1;
next	5.7;

5.7
date	2015.06.29.02.44.16;	author mcalabre;	state Exp;
branches
	5.7.1.1;
next	5.6;

5.6
date	2015.06.14.07.11.24;	author mcalabre;	state Exp;
branches
	5.6.1.1;
next	5.5;

5.5
date	2015.05.05.13.16.31;	author mcalabre;	state Exp;
branches
	5.5.1.1;
next	5.4;

5.4
date	2015.04.21.14.41.05;	author mcalabre;	state Exp;
branches
	5.4.1.1;
next	5.3;

5.3
date	2015.04.21.02.50.51;	author mcalabre;	state Exp;
branches
	5.3.1.1;
next	5.2;

5.2
date	2015.04.15.12.35.07;	author mcalabre;	state Exp;
branches
	5.2.1.1;
next	5.1;

5.1
date	2015.04.07.10.56.36;	author mcalabre;	state Exp;
branches
	5.1.1.1;
next	5.0;

5.0
date	2015.04.05.12.24.59;	author mcalabre;	state Exp;
branches
	5.0.1.1;
next	4.25;

4.25
date	2014.12.14.14.29.36;	author mcalabre;	state Exp;
branches
	4.25.1.1;
next	4.24;

4.24
date	2014.09.18.15.25.00;	author mcalabre;	state Exp;
branches
	4.24.1.1;
next	4.23;

4.23
date	2014.05.13.05.50.51;	author mcalabre;	state Exp;
branches
	4.23.1.1;
next	4.22;

4.22
date	2014.04.12.15.03.52;	author mcalabre;	state Exp;
branches
	4.22.1.1;
next	4.21;

4.21
date	2014.03.24.05.12.27;	author mcalabre;	state Exp;
branches
	4.21.1.1;
next	4.20;

4.20
date	2013.12.18.05.42.49;	author mcalabre;	state Exp;
branches
	4.20.1.1;
next	4.19;

4.19
date	2013.09.29.14.17.51;	author mcalabre;	state Exp;
branches
	4.19.1.1;
next	4.18;

4.18
date	2013.07.13.10.00.04;	author mcalabre;	state Exp;
branches
	4.18.1.1;
next	4.17;

4.17
date	2013.01.29.05.29.20;	author cal103;	state Exp;
branches
	4.17.1.1;
next	4.16;

4.16
date	2012.11.07.04.42.44;	author cal103;	state Exp;
branches
	4.16.1.1;
next	4.15;

4.15
date	2012.09.26.14.26.05;	author cal103;	state Exp;
branches
	4.15.1.1;
next	4.14;

4.14
date	2012.07.13.10.02.27;	author cal103;	state Exp;
branches
	4.14.1.1;
next	4.13;

4.13
date	2012.03.14.07.39.30;	author cal103;	state Exp;
branches
	4.13.1.1;
next	4.12;

4.12
date	2012.02.29.07.57.56;	author cal103;	state Exp;
branches
	4.12.1.1;
next	4.11;

4.11
date	2012.02.20.23.30.02;	author cal103;	state Exp;
branches
	4.11.1.1;
next	4.10;

4.10
date	2012.02.05.23.41.44;	author cal103;	state Exp;
branches
	4.10.1.1;
next	4.9;

4.9
date	2012.01.24.05.40.29;	author cal103;	state Exp;
branches
	4.9.1.1;
next	4.8;

4.8
date	2011.08.15.08.05.53;	author cal103;	state Exp;
branches
	4.8.1.1;
next	4.7;

4.7
date	2011.02.07.07.03.42;	author cal103;	state Exp;
branches
	4.7.1.1;
next	4.6;

4.6
date	2010.11.16.06.15.31;	author cal103;	state Exp;
branches
	4.6.1.1;
next	4.5;

4.5
date	2010.07.16.07.01.25;	author cal103;	state Exp;
branches
	4.5.1.1;
next	4.4;

4.4
date	2009.08.06.07.41.33;	author cal103;	state Exp;
branches
	4.4.1.1;
next	4.3;

4.3
date	2007.12.27.05.41.36;	author cal103;	state Exp;
branches
	4.3.1.1;
next	4.2;

4.2
date	2005.09.22.09.14.18;	author cal103;	state Exp;
branches
	4.2.1.1;
next	4.1;

4.1
date	2005.08.25.13.10.40;	author cal103;	state Exp;
branches;
next	4.0;

4.0
date	2005.02.04.05.06.59;	author cal103;	state Exp;
branches;
next	3.6;

3.6
date	2004.08.25.04.10.18;	author cal103;	state Exp;
branches;
next	3.5;

3.5
date	2004.06.28.01.45.40;	author mcalabre;	state Exp;
branches;
next	3.4;

3.4
date	2004.02.11.00.15.03;	author mcalabre;	state Exp;
branches;
next	3.3;

3.3
date	2003.10.26.23.03.59;	author mcalabre;	state Exp;
branches;
next	1.5;

1.5
date	2003.10.21.08.52.49;	author mcalabre;	state Exp;
branches;
next	1.4;

1.4
date	2003.09.09.04.04.04;	author mcalabre;	state Exp;
branches;
next	1.3;

1.3
date	2003.04.28.03.50.41;	author mcalabre;	state Exp;
branches;
next	1.2;

1.2
date	2002.11.06.06.47.52;	author mcalabre;	state Exp;
branches;
next	1.1;

1.1
date	2002.11.06.00.39.55;	author mcalabre;	state Exp;
branches;
next	;

4.2.1.1
date	2005.12.05.02.39.43;	author cal103;	state Exp;
branches;
next	4.2.1.2;

4.2.1.2
date	2007.06.29.00.58.20;	author cal103;	state Exp;
branches;
next	4.2.1.3;

4.2.1.3
date	2007.08.02.04.27.06;	author cal103;	state Exp;
branches;
next	4.2.1.4;

4.2.1.4
date	2007.12.03.05.02.17;	author cal103;	state Exp;
branches;
next	;

4.3.1.1
date	2007.12.27.13.23.47;	author cal103;	state Exp;
branches;
next	4.3.1.2;

4.3.1.2
date	2009.07.22.04.28.39;	author cal103;	state Exp;
branches;
next	4.3.1.3;

4.3.1.3
date	2009.07.28.07.45.25;	author cal103;	state Exp;
branches;
next	;

4.4.1.1
date	2009.08.10.08.52.49;	author cal103;	state Exp;
branches;
next	4.4.1.2;

4.4.1.2
date	2010.01.19.03.42.50;	author cal103;	state Exp;
branches;
next	4.4.1.3;

4.4.1.3
date	2010.07.07.13.34.29;	author cal103;	state Exp;
branches;
next	4.4.1.4;

4.4.1.4
date	2010.07.14.07.58.12;	author cal103;	state Exp;
branches;
next	;

4.5.1.1
date	2010.07.16.07.05.31;	author cal103;	state Exp;
branches;
next	4.5.1.2;

4.5.1.2
date	2010.09.23.01.32.16;	author cal103;	state Exp;
branches;
next	;

4.6.1.1
date	2010.11.16.06.16.19;	author cal103;	state Exp;
branches;
next	4.6.1.2;

4.6.1.2
date	2011.01.14.06.33.16;	author cal103;	state Exp;
branches;
next	;

4.7.1.1
date	2011.02.07.07.04.22;	author cal103;	state Exp;
branches;
next	4.7.1.2;

4.7.1.2
date	2011.07.21.03.48.09;	author cal103;	state Exp;
branches;
next	4.7.1.3;

4.7.1.3
date	2011.08.09.02.43.50;	author cal103;	state Exp;
branches;
next	4.7.1.4;

4.7.1.4
date	2011.08.10.05.50.57;	author cal103;	state Exp;
branches;
next	;

4.8.1.1
date	2011.08.15.08.07.06;	author cal103;	state Exp;
branches;
next	4.8.1.2;

4.8.1.2
date	2011.11.17.03.19.58;	author cal103;	state Exp;
branches;
next	4.8.1.3;

4.8.1.3
date	2012.01.19.05.09.30;	author cal103;	state Exp;
branches;
next	4.8.1.4;

4.8.1.4
date	2012.01.23.06.10.06;	author cal103;	state Exp;
branches;
next	4.8.1.5;

4.8.1.5
date	2012.01.23.06.50.47;	author cal103;	state Exp;
branches;
next	4.8.1.6;

4.8.1.6
date	2012.01.24.05.23.06;	author cal103;	state Exp;
branches;
next	;

4.9.1.1
date	2012.01.24.05.41.45;	author cal103;	state Exp;
branches;
next	4.9.1.2;

4.9.1.2
date	2012.02.01.00.09.53;	author cal103;	state Exp;
branches;
next	4.9.1.3;

4.9.1.3
date	2012.02.02.08.23.24;	author cal103;	state Exp;
branches;
next	;

4.10.1.1
date	2012.02.05.23.42.46;	author cal103;	state Exp;
branches;
next	;

4.11.1.1
date	2012.02.20.23.31.13;	author cal103;	state Exp;
branches;
next	;

4.12.1.1
date	2012.02.29.07.59.10;	author cal103;	state Exp;
branches;
next	;

4.13.1.1
date	2012.03.14.07.40.37;	author cal103;	state Exp;
branches;
next	4.13.1.2;

4.13.1.2
date	2012.07.12.15.20.28;	author cal103;	state Exp;
branches;
next	4.13.1.3;

4.13.1.3
date	2012.07.13.03.34.20;	author cal103;	state Exp;
branches;
next	4.13.1.4;

4.13.1.4
date	2012.07.13.08.50.01;	author cal103;	state Exp;
branches;
next	;

4.14.1.1
date	2012.07.13.10.04.44;	author cal103;	state Exp;
branches;
next	;

4.15.1.1
date	2012.09.26.14.27.51;	author cal103;	state Exp;
branches;
next	;

4.16.1.1
date	2012.11.07.04.45.56;	author cal103;	state Exp;
branches;
next	4.16.1.2;

4.16.1.2
date	2012.11.28.01.24.26;	author cal103;	state Exp;
branches;
next	4.16.1.3;

4.16.1.3
date	2013.01.29.05.09.44;	author cal103;	state Exp;
branches;
next	;

4.17.1.1
date	2013.01.29.05.32.32;	author cal103;	state Exp;
branches;
next	4.17.1.2;

4.17.1.2
date	2013.06.10.03.48.15;	author mcalabre;	state Exp;
branches;
next	;

4.18.1.1
date	2013.07.13.10.02.37;	author mcalabre;	state Exp;
branches;
next	;

4.19.1.1
date	2013.09.29.14.20.38;	author mcalabre;	state Exp;
branches;
next	4.19.1.2;

4.19.1.2
date	2013.12.17.15.01.43;	author mcalabre;	state Exp;
branches;
next	;

4.20.1.1
date	2013.12.18.05.46.55;	author mcalabre;	state Exp;
branches;
next	4.20.1.2;

4.20.1.2
date	2014.03.21.06.32.19;	author mcalabre;	state Exp;
branches;
next	;

4.21.1.1
date	2014.03.24.05.15.28;	author mcalabre;	state Exp;
branches;
next	;

4.22.1.1
date	2014.04.12.15.06.32;	author mcalabre;	state Exp;
branches;
next	;

4.23.1.1
date	2014.05.13.05.53.49;	author mcalabre;	state Exp;
branches;
next	;

4.24.1.1
date	2014.09.18.15.28.09;	author mcalabre;	state Exp;
branches;
next	;

4.25.1.1
date	2014.12.14.14.32.45;	author mcalabre;	state Exp;
branches;
next	4.25.1.2;

4.25.1.2
date	2015.01.06.01.01.06;	author mcalabre;	state Exp;
branches;
next	4.25.1.3;

4.25.1.3
date	2015.04.03.01.36.54;	author mcalabre;	state Exp;
branches;
next	;

5.0.1.1
date	2015.04.05.12.28.00;	author mcalabre;	state Exp;
branches;
next	;

5.1.1.1
date	2015.04.07.11.01.10;	author mcalabre;	state Exp;
branches;
next	5.1.1.2;

5.1.1.2
date	2015.04.13.12.25.22;	author mcalabre;	state Exp;
branches;
next	;

5.2.1.1
date	2015.04.15.12.39.14;	author mcalabre;	state Exp;
branches;
next	;

5.3.1.1
date	2015.04.21.02.54.02;	author mcalabre;	state Exp;
branches;
next	;

5.4.1.1
date	2015.04.21.14.44.28;	author mcalabre;	state Exp;
branches;
next	;

5.5.1.1
date	2015.05.05.13.21.00;	author mcalabre;	state Exp;
branches;
next	;

5.6.1.1
date	2015.06.14.07.14.44;	author mcalabre;	state Exp;
branches;
next	;

5.7.1.1
date	2015.06.29.02.47.24;	author mcalabre;	state Exp;
branches;
next	;

5.8.1.1
date	2015.07.08.11.07.18;	author mcalabre;	state Exp;
branches;
next	;

5.9.1.1
date	2015.07.21.09.28.07;	author mcalabre;	state Exp;
branches;
next	;

5.10.1.1
date	2015.10.09.08.22.35;	author mcalabre;	state Exp;
branches;
next	;

5.11.1.1
date	2015.10.18.09.16.27;	author mcalabre;	state Exp;
branches;
next	;

5.12.1.1
date	2015.11.15.02.16.14;	author mcalabre;	state Exp;
branches;
next	5.12.1.2;

5.12.1.2
date	2016.01.25.05.17.04;	author mcalabre;	state Exp;
branches;
next	5.12.1.3;

5.12.1.3
date	2016.01.25.12.22.41;	author mcalabre;	state Exp;
branches;
next	;

5.13.1.1
date	2016.01.26.08.54.32;	author mcalabre;	state Exp;
branches;
next	;

5.14.1.1
date	2016.02.07.10.52.44;	author mcalabre;	state Exp;
branches;
next	5.14.1.2;

5.14.1.2
date	2016.03.21.06.10.32;	author mcalabre;	state Exp;
branches;
next	;

5.15.1.1
date	2016.04.05.12.58.35;	author mcalabre;	state Exp;
branches;
next	5.15.1.2;

5.15.1.2
date	2017.01.15.02.14.17;	author mcalabre;	state Exp;
branches;
next	5.15.1.3;

5.15.1.3
date	2017.01.15.04.13.33;	author mcalabre;	state Exp;
branches;
next	;

5.16.1.1
date	2017.01.15.04.28.59;	author mcalabre;	state Exp;
branches;
next	5.16.1.2;

5.16.1.2
date	2017.09.12.14.15.32;	author mcalabre;	state Exp;
branches;
next	;

5.17.1.1
date	2017.09.18.08.45.59;	author mcalabre;	state Exp;
branches;
next	5.17.1.2;

5.17.1.2
date	2018.01.10.08.25.34;	author mcalabre;	state Exp;
branches;
next	;

5.18.1.1
date	2018.01.10.08.33.04;	author mcalabre;	state Exp;
branches;
next	;

5.19.1.1
date	2018.07.26.15.41.40;	author mcalabre;	state Exp;
branches;
next	;

5.20.1.1
date	2018.10.05.05.33.33;	author mcalabre;	state Exp;
branches;
next	5.20.1.2;

5.20.1.2
date	2018.10.19.07.03.04;	author mcalabre;	state Exp;
branches;
next	;

6.1.1.1
date	2018.10.19.08.36.15;	author mcalabre;	state Exp;
branches;
next	;

6.2.1.1
date	2018.10.20.10.04.15;	author mcalabre;	state Exp;
branches;
next	6.2.1.2;

6.2.1.2
date	2019.05.10.01.56.50;	author mcalabre;	state Exp;
branches;
next	6.2.1.3;

6.2.1.3
date	2019.07.08.03.39.51;	author mcalabre;	state Exp;
branches;
next	6.2.1.4;

6.2.1.4
date	2019.07.08.05.14.24;	author mcalabre;	state Exp;
branches;
next	;

6.3.1.1
date	2019.07.12.07.34.37;	author mcalabre;	state Exp;
branches;
next	;

6.4.1.1
date	2019.08.15.09.31.29;	author mcalabre;	state Exp;
branches;
next	6.4.1.2;

6.4.1.2
date	2019.12.01.05.25.19;	author mcalabre;	state Exp;
branches;
next	6.4.1.3;

6.4.1.3
date	2019.12.12.02.32.25;	author mcalabre;	state Exp;
branches;
next	6.4.1.4;

6.4.1.4
date	2019.12.16.04.38.47;	author mcalabre;	state Exp;
branches;
next	6.4.1.5;

6.4.1.5
date	2019.12.31.03.28.03;	author mcalabre;	state Exp;
branches;
next	;

7.1.1.1
date	2019.12.31.13.28.19;	author mcalabre;	state Exp;
branches;
next	;

7.2.1.1
date	2020.03.09.07.34.50;	author mcalabre;	state Exp;
branches;
next	7.2.1.2;

7.2.1.2
date	2020.06.03.03.25.37;	author mcalabre;	state Exp;
branches;
next	;

7.3.1.1
date	2020.06.03.03.38.10;	author mcalabre;	state Exp;
branches;
next	7.3.1.2;

7.3.1.2
date	2020.08.17.12.10.44;	author mcalabre;	state Exp;
branches;
next	7.3.1.3;

7.3.1.3
date	2020.08.17.12.51.45;	author mcalabre;	state Exp;
branches;
next	7.3.1.4;

7.3.1.4
date	2021.01.19.14.09.10;	author mcalabre;	state Exp;
branches;
next	7.3.1.5;

7.3.1.5
date	2021.01.28.12.29.30;	author mcalabre;	state Exp;
branches;
next	;

7.4.1.1
date	2021.01.31.02.26.24;	author mcalabre;	state Exp;
branches;
next	;

7.5.1.1
date	2021.03.20.05.56.42;	author mcalabre;	state Exp;
branches;
next	7.5.1.2;

7.5.1.2
date	2021.04.09.02.49.00;	author mcalabre;	state Exp;
branches;
next	7.5.1.3;

7.5.1.3
date	2021.04.10.14.26.36;	author mcalabre;	state Exp;
branches;
next	7.5.1.4;

7.5.1.4
date	2021.04.10.15.42.19;	author mcalabre;	state Exp;
branches;
next	7.5.1.5;

7.5.1.5
date	2021.04.13.09.36.00;	author mcalabre;	state Exp;
branches;
next	;

7.6.1.1
date	2021.04.13.13.00.35;	author mcalabre;	state Exp;
branches;
next	7.6.1.2;

7.6.1.2
date	2021.05.13.03.30.55;	author mcalabre;	state Exp;
branches;
next	7.6.1.3;

7.6.1.3
date	2021.05.13.03.53.56;	author mcalabre;	state Exp;
branches;
next	;

7.7.1.1
date	2021.07.12.06.38.27;	author mcalabre;	state Exp;
branches;
next	7.7.1.2;

7.7.1.2
date	2022.03.25.06.05.59;	author mcalabre;	state Exp;
branches;
next	;

7.8.1.1
date	2022.03.25.06.14.14;	author mcalabre;	state Exp;
branches;
next	;

7.9.1.1
date	2022.03.25.15.16.40;	author mcalabre;	state Exp;
branches;
next	;

7.10.1.1
date	2022.04.23.14.26.54;	author mcalabre;	state Exp;
branches;
next	;

7.11.1.1
date	2022.04.26.06.15.32;	author mcalabre;	state Exp;
branches;
next	;

7.12.1.1
date	2022.09.09.05.00.36;	author mcalabre;	state Exp;
branches;
next	;

7.13.1.1
date	2022.10.07.03.45.47;	author mcalabre;	state Exp;
branches;
next	7.13.1.2;

7.13.1.2
date	2023.06.15.04.17.19;	author mcalabre;	state Exp;
branches;
next	;

8.0.1.1
date	2023.07.01.06.22.44;	author mcalabre;	state Exp;
branches;
next	;

8.1.1.1
date	2023.07.05.17.15.00;	author mcalabre;	state Exp;
branches;
next	;

8.2.1.1
date	2023.11.16.10.05.57;	author mcalabre;	state Exp;
branches;
next	8.2.1.2;

8.2.1.2
date	2023.11.29.07.40.24;	author mcalabre;	state Exp;
branches;
next	8.2.1.3;

8.2.1.3
date	2024.04.02.14.01.58;	author mcalabre;	state Exp;
branches;
next	8.2.1.4;

8.2.1.4
date	2024.05.13.05.14.44;	author mcalabre;	state Exp;
branches;
next	;

8.3.1.1
date	2024.05.13.16.35.39;	author mcalabre;	state Exp;
branches;
next	;

8.4.1.1
date	2024.10.28.13.58.41;	author mcalabre;	state Exp;
branches;
next	8.4.1.2;

8.4.1.2
date	2025.12.06.13.37.16;	author mcalabre;	state Exp;
branches;
next	;

8.5.1.1
date	2025.12.06.14.05.15;	author mcalabre;	state Exp;
branches;
next	8.5.1.2;

8.5.1.2
date	2026.03.29.13.36.16;	author mcalabre;	state Exp;
branches;
next	;

8.6.1.1
date	2026.03.29.14.19.26;	author mcalabre;	state Exp;
branches;
next	8.6.1.2;

8.6.1.2
date	2026.04.05.08.35.50;	author mcalabre;	state Exp;
branches;
next	8.6.1.3;

8.6.1.3
date	2026.04.10.09.03.47;	author mcalabre;	state Exp;
branches;
next	8.6.1.4;

8.6.1.4
date	2026.04.10.09.23.11;	author mcalabre;	state Exp;
branches;
next	8.6.1.5;

8.6.1.5
date	2026.04.22.13.13.01;	author mcalabre;	state Exp;
branches;
next	8.6.1.6;

8.6.1.6
date	2026.04.22.15.15.05;	author mcalabre;	state Exp;
branches;
next	8.6.1.7;

8.6.1.7
date	2026.05.11.11.31.14;	author mcalabre;	state Exp;
branches;
next	;


desc
@Implementation of functions that identify and translate various forms of non-
standard construct that are known to occur in FITS WCS headers.  These range
from the translation of non-standard values for standard WCS keywords, to the
repair of malformed coordinate representations.
@


8.7
log
@WCSLIB 8.7
@
text
@/*============================================================================
  WCSLIB 8.7 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2026, Mark Calabretta

  This file is part of WCSLIB.

  WCSLIB is free software: you can redistribute it and/or modify it under the
  terms of the GNU Lesser General Public License as published by the Free
  Software Foundation, either version 3 of the License, or (at your option)
  any later version.

  WCSLIB 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 Lesser General Public License for
  more details.

  You should have received a copy of the GNU Lesser General Public License
  along with WCSLIB.  If not, see http://www.gnu.org/licenses.

  Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
  http://www.atnf.csiro.au/computing/software/wcs
  $Id: wcsfix.c,v 8.6.1.7 2026/05/11 11:31:14 mcalabre Exp mcalabre $
*===========================================================================*/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lin.h"
#include "sph.h"
#include "tab.h"
#include "wcs.h"
#include "wcserr.h"
#include "wcsfix.h"
#include "wcslimits.h"
#include "wcsmath.h"
#include "wcstrig.h"
#include "wcsunits.h"
#include "wcsutil.h"
#include "wtbarr.h"

// Map status return value to message.
const char *wcsfix_errmsg[] = {
  "Success",
  "Null wcsprm pointer passed",
  "Memory allocation failed",
  "Linear transformation matrix is singular",
  "Inconsistent or unrecognized coordinate axis types",
  "Invalid parameter value",
  "Invalid coordinate transformation parameters",
  "Ill-conditioned coordinate transformation parameters",
  "All of the corner pixel coordinates are invalid",
  "Could not determine reference pixel coordinate",
  "Could not determine reference pixel value"};

// Map error returns for lower-level routines.
const int fix_linerr[] = {
  FIXERR_SUCCESS,		//  0: LINERR_SUCCESS
  FIXERR_NULL_POINTER,		//  1: LINERR_NULL_POINTER
  FIXERR_MEMORY,		//  2: LINERR_MEMORY
  FIXERR_SINGULAR_MTX,		//  3: LINERR_SINGULAR_MTX
  FIXERR_BAD_PARAM,		//  4: LINERR_DISTORT_INIT
  FIXERR_NO_REF_PIX_COORD,	//  5: LINERR_DISTORT
  FIXERR_NO_REF_PIX_VAL		//  6: LINERR_DEDISTORT
};

const int fix_wcserr[] = {
  FIXERR_SUCCESS,		//  0: WCSERR_SUCCESS
  FIXERR_NULL_POINTER,		//  1: WCSERR_NULL_POINTER
  FIXERR_MEMORY,		//  2: WCSERR_MEMORY
  FIXERR_SINGULAR_MTX,		//  3: WCSERR_SINGULAR_MTX
  FIXERR_BAD_CTYPE,		//  4: WCSERR_BAD_CTYPE
  FIXERR_BAD_PARAM,		//  5: WCSERR_BAD_PARAM
  FIXERR_BAD_COORD_TRANS,	//  6: WCSERR_BAD_COORD_TRANS
  FIXERR_ILL_COORD_TRANS,	//  7: WCSERR_ILL_COORD_TRANS
  FIXERR_BAD_CORNER_PIX,	//  8: WCSERR_BAD_PIX
  FIXERR_NO_REF_PIX_VAL,	//  9: WCSERR_BAD_WORLD
  FIXERR_NO_REF_PIX_VAL 	// 10: WCSERR_BAD_WORLD_COORD
				//     ...others not used
};

static const int WCSSET = 137;		// Matching wcs.c

// Convenience macro for invoking wcserr_set().
#define WCSFIX_ERRMSG(status) WCSERR_SET(status), wcsfix_errmsg[status]

//----------------------------------------------------------------------------

int wcsfix(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[])

{
  int status = 0;

  if ((stat[CDFIX] = cdfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[DATFIX] = datfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[OBSFIX] = obsfix(0, wcs)) > 0) {
    status = 1;
  }

  if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) {
    status = 1;
  }

  if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[CELFIX] = celfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
    status = 1;
  }

  return status;
}

//----------------------------------------------------------------------------

int wcsfixi(
  int ctrl,
  const int naxis[],
  struct wcsprm *wcs,
  int stat[],
  struct wcserr info[])

{
  int status = 0;

  // Handling the status values returned from the sub-fixers is trickier than
  // it might seem, especially considering that wcs->err may contain an error
  // status on input which should be preserved if no translation errors occur.
  // The simplest way seems to be to save a copy of wcs->err and clear it
  // before each sub-fixer.  The last real error to occur, excluding
  // informative messages, is the one returned.

  // To get informative messages from spcfix() it must precede celfix() and
  // cylfix().  The latter call wcsset() which also translates AIPS-convention
  // spectral axes.
  struct wcserr err;
  wcserr_copy(wcs->err, &err);

  for (int ifix = CDFIX; ifix < NWCSFIX; ifix++) {
    // Clear (delete) wcs->err.
    wcserr_clear(&(wcs->err));

    switch (ifix) {
    case CDFIX:
      stat[ifix] = cdfix(wcs);
      break;
    case DATFIX:
      stat[ifix] = datfix(wcs);
      break;
    case OBSFIX:
      stat[ifix] = obsfix(0, wcs);
      break;
    case UNITFIX:
      stat[ifix] = unitfix(ctrl, wcs);
      break;
    case SPCFIX:
      stat[ifix] = spcfix(wcs);
      break;
    case CELFIX:
      stat[ifix] = celfix(wcs);
      break;
    case CYLFIX:
      stat[ifix] = cylfix(naxis, wcs);
      break;
    default:
      continue;
    }

    if (stat[ifix] == FIXERR_NO_CHANGE) {
      // No change => no message.
      wcserr_copy(0x0, info+ifix);

    } else if (stat[ifix] == 0) {
      // Successful translation, but there may be an informative message.
      if (wcs->err && wcs->err->status < 0) {
        wcserr_copy(wcs->err, info+ifix);
      } else {
        wcserr_copy(0x0, info+ifix);
      }

    } else {
      // An informative message or error message.
      wcserr_copy(wcs->err, info+ifix);

      if ((status = (stat[ifix] > 0))) {
        // It was an error, replace the previous one.
        wcserr_copy(wcs->err, &err);
      }
    }
  }

  // Restore the last error to occur.
  if (err.status) {
    wcserr_copy(&err, wcs->err);
  } else {
    wcserr_clear(&(wcs->err));
  }

  return status;
}

//----------------------------------------------------------------------------

int cdfix(struct wcsprm *wcs)

{
  if (wcs == 0x0) return FIXERR_NULL_POINTER;

  if ((wcs->altlin & 1) || !(wcs->altlin & 2)) {
    // Either we have PCi_ja or there are no CDi_ja.
    return FIXERR_NO_CHANGE;
  }

  int naxis  = wcs->naxis;
  int status = FIXERR_NO_CHANGE;
  for (int i = 0; i < naxis; i++) {
    // Row of zeros?
    double *cd = wcs->cd + (size_t)(i*naxis);
    for (int k = 0; k < naxis; k++, cd++) {
      if (*cd != 0.0) goto next;
    }

    // Column of zeros?
    cd = wcs->cd + i;
    for (int k = 0; k < naxis; k++, cd += naxis) {
      if (*cd != 0.0) goto next;
    }

    cd = wcs->cd + (size_t)(i*(naxis + 1));
    *cd = 1.0;
    status = FIXERR_SUCCESS;

next: ;
  }

  return status;
}

//----------------------------------------------------------------------------

static int parse_date(const char *buf, int *hour, int *minute, double *sec)

{
  char ctmp[72];
  if (sscanf(buf, "%2d:%2d:%s", hour, minute, ctmp) < 3 ||
      wcsutil_str2double(ctmp, sec)) {
    return 1;
  }

  return 0;
}


static void write_date(char buf[32], int hour, int minute, double sec)

{
  char ctmp[32];
  wcsutil_double2str(ctmp, 32, "%04.1f", sec);
  snprintf(buf, 32, "T%.2d:%.2d:%.5s", hour, minute, ctmp);
}


static char *newline(char **cp, char *cpe)

{
  size_t k = strlen(*cp);
  if (k) {
    *cp += k;
    strncpy(*cp, ".\n", cpe - *cp);
    *cp += 2;
  }

  return *cp;
}


int datfix(struct wcsprm *wcs)

{
  static const char *function = "datfix";

  // MJD of J2000.0 and B1900.0.
  const double mjd2000 = 51544.5;
  const double mjd1900 = 15019.81352;

  // Days per Julian year and per tropical year.
  const double djy = 365.25;
  const double dty = 365.242198781;

  int  day, hour = 0, minute = 0, month, year;
  double sec = 0.0;

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  char infomsg[512], *cp = infomsg, *cpe = infomsg + 512;
  infomsg[0] = '\0';
  size_t csize;

  int status = FIXERR_NO_CHANGE;

  for (int i = 0; i < 5; i++) {
    // MJDREF is split into integer and fractional parts, wheres MJDOBS and
    // the rest are a single value.
    const char *dateid = 0x0;
    char *date = 0x0;
    double *wcsmjd = 0x0;
    if (i == 0) {
      // Note, DATEREF and MJDREF, not DATE-REF and MJD-REF (sigh).
      dateid = "REF";
      date   = wcs->dateref;
      wcsmjd = wcs->mjdref;
    } else if (i == 1) {
      dateid = "-OBS";
      date   = wcs->dateobs;
      wcsmjd = &(wcs->mjdobs);
    } else if (i == 2) {
      dateid = "-BEG";
      date   = wcs->datebeg;
      wcsmjd = &(wcs->mjdbeg);
    } else if (i == 3) {
      dateid = "-AVG";
      date   = wcs->dateavg;
      wcsmjd = &(wcs->mjdavg);
    } else if (i == 4) {
      dateid = "-END";
      date   = wcs->dateend;
      wcsmjd = &(wcs->mjdend);
    }

    char orig_date[72];
    strncpy(orig_date, date, 72);
    orig_date[71] = '\0';

    if (date[0] == '\0') {
      // Fill in DATE from MJD if possible.

      if (i == 1 && undefined(*wcsmjd)) {
        // See if we have jepoch or bepoch.
        if (!undefined(wcs->jepoch)) {
          *wcsmjd = mjd2000 + (wcs->jepoch - 2000.0)*djy;
          newline(&cp, cpe);
          csize = cpe - cp;
          snprintf(cp, csize, "Set MJD-OBS to %.6f from JEPOCH", *wcsmjd);
          if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;

        } else if (!undefined(wcs->bepoch)) {
          *wcsmjd = mjd1900 + (wcs->bepoch - 1900.0)*dty;
          newline(&cp, cpe);
          csize = cpe - cp;
          snprintf(cp, csize, "Set MJD-OBS to %.6f from BEPOCH", *wcsmjd);
          if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
        }
      }

      if (undefined(*wcsmjd)) {
        // No date information was provided.

      } else {
        // Calendar date from MJD, with allowance for MJD < 0.
        double mjd[2], t;
        if (i == 0) {
          // MJDREF is already split into integer and fractional parts.
          mjd[0] = wcsmjd[0];
          mjd[1] = wcsmjd[1];
          if (1.0 < mjd[1]) {
            // Ensure the fractional part lies between 0 and +1.
            t = floor(mjd[1]);
            mjd[0] += t;
            mjd[1] -= t;
          }
        } else {
          // Split it into integer and fractional parts.
          mjd[0] = floor(*wcsmjd);
          mjd[1] = *wcsmjd - mjd[0];
        }

        int jd = 2400001 + (int)mjd[0];

        int n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
        int dd = 10*(((n4-237)%1461)/4) + 5;

        year  = n4/1461 - 4712;
        month = (2 + dd/306)%12 + 1;
        day   = (dd%306)/10 + 1;
        snprintf(date, 72, "%.4d-%.2d-%.2d", year, month, day);

        // Write time part only if non-zero.
        if (0.0 < (t = mjd[1])) {
          t *= 24.0;
          hour = (int)t;
          t = 60.0 * (t - hour);
          minute = (int)t;
          sec    = 60.0 * (t - minute);

          // Round to 1ms.
          dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005));
          hour = dd / 3600000;
          dd -= 3600000 * hour;
          minute = dd / 60000;
          int msec = dd - 60000 * minute;
          snprintf(date+10, 62, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);

          // Write fractions of a second only if non-zero.
          if (msec%1000) {
            snprintf(date+19, 53, ".%.3d", msec%1000);
          }
        }
      }

    } else {
      if (strlen(date) < 8) {
        // Can't be a valid date.
        status = FIXERR_BAD_PARAM;
        newline(&cp, cpe);
        csize = cpe - cp;
        snprintf(cp, csize, "Invalid DATE%s format '%s' is too short",
          dateid, date);
        continue;
      }

      // Identify the date format.
      if (date[4] == '-' && date[7] == '-') {
        // Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]]
        if (sscanf(date, "%4d-%2d-%2d", &year, &month, &day) < 3) {
          status = FIXERR_BAD_PARAM;
          newline(&cp, cpe);
          csize = cpe - cp;
          snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid, date);
          continue;
        }

        if (date[10] == 'T') {
          if (parse_date(date+11, &hour, &minute, &sec)) {
            status = FIXERR_BAD_PARAM;
            newline(&cp, cpe);
            csize = cpe - cp;
            snprintf(cp, csize, "Invalid time in DATE%s '%s'", dateid,
              date+11);
            continue;
          }
        } else if (date[10] == ' ') {
          hour = 0;
          minute = 0;
          sec = 0.0;
          if (parse_date(date+11, &hour, &minute, &sec)) {
            write_date(date+10, hour, minute, sec);
          } else {
            date[10] = 'T';
          }
        }

      } else if (date[4] == '/' && date[7] == '/') {
        // Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]]
        if (sscanf(date, "%4d/%2d/%2d", &year, &month, &day) < 3) {
          status = FIXERR_BAD_PARAM;
          newline(&cp, cpe);
          csize = cpe - cp;
          snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid, date);
          continue;
        }

        if (date[10] == 'T') {
          if (parse_date(date+11, &hour, &minute, &sec)) {
            status = FIXERR_BAD_PARAM;
            newline(&cp, cpe);
            csize = cpe - cp;
            snprintf(cp, csize, "Invalid time in DATE%s '%s'", dateid,
              date+11);
            continue;
          }
        } else if (date[10] == ' ') {
          hour = 0;
          minute = 0;
          sec = 0.0;
          if (parse_date(date+11, &hour, &minute, &sec)) {
            write_date(date+10, hour, minute, sec);
          } else {
            date[10] = 'T';
          }
        }

        // Looks ok, fix it up.
        date[4]  = '-';
        date[7]  = '-';

      } else {
        if (i == 1 && date[2] == '/' && date[5] == '/') {
          // Old format DATE-OBS date: DD/MM/YY, also allowing DD/MM/CCYY.
          if (sscanf(date, "%2d/%2d/%4d", &day, &month, &year) < 3) {
            status = FIXERR_BAD_PARAM;
            newline(&cp, cpe);
            csize = cpe - cp;
            snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid,
              date);
            continue;
          }

        } else if (i == 1 && date[2] == '-' && date[5] == '-') {
          // Also recognize DD-MM-YY and DD-MM-CCYY
          if (sscanf(date, "%2d-%2d-%4d", &day, &month, &year) < 3) {
            status = FIXERR_BAD_PARAM;
            newline(&cp, cpe);
            csize = cpe - cp;
            snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid,
              date);
            continue;
          }

        } else {
          // Not a valid date format.
          status = FIXERR_BAD_PARAM;
          newline(&cp, cpe);
          csize = cpe - cp;
          snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid, date);
          continue;
        }

        if (year < 100) year += 1900;

        // Doesn't have a time.
        snprintf(date, 72, "%.4d-%.2d-%.2d", year, month, day);
      }

      // Compute MJD using Hatcher's algorithm, based on integer division.
      double mjd[2];
      int m = (1461*(year - (12-month)/10 + 4712))/4
               + (306*((month+9)%12) + 5)/10
               - (3*((year - (12-month)/10 + 4900)/100))/4
               + day - 2399904;
      mjd[0] = (double)m;
      mjd[1] = (hour + (minute + sec/60.0)/60.0)/24.0;
      double mjdsum = mjd[0] + mjd[1];

      if (undefined(*wcsmjd)) {
        if (i == 0) {
          wcsmjd[0] = mjd[0];
          wcsmjd[1] = mjd[1];
        } else {
          *wcsmjd = mjdsum;
        }
        newline(&cp, cpe);
        csize = cpe - cp;
        snprintf(cp, csize, "Set MJD%s to %.6f from DATE%s", dateid,
          mjdsum, dateid);

        if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;

      } else {
        // Check for consistency.
        double mjdtmp;
        if (i == 0) {
          mjdtmp = wcsmjd[0] + wcsmjd[1];
        } else {
          mjdtmp = *wcsmjd;
        }

        if (0.001 < fabs(mjdsum - mjdtmp)) {
          status = FIXERR_BAD_PARAM;
          newline(&cp, cpe);
          csize = cpe - cp;
          snprintf(cp, csize,
            "Invalid parameter values: MJD%s and DATE%s are inconsistent",
            dateid, dateid);
        }
      }

      if (i == 1) {
        if (!undefined(wcs->jepoch)) {
          // Check consistency of JEPOCH.
          double jepoch = 2000.0 + (*wcsmjd - mjd2000) / djy;

          if (0.000002 < fabs(jepoch - wcs->jepoch)) {
            // Informational only, no error.
            newline(&cp, cpe);
            csize = cpe - cp;
            snprintf(cp, csize, "JEPOCH is inconsistent with DATE-OBS");
          }
        }

        if (!undefined(wcs->bepoch)) {
          // Check consistency of BEPOCH.
          double bepoch = 1900.0 + (*wcsmjd - mjd1900) / dty;

          if (0.000002 < fabs(bepoch - wcs->bepoch)) {
            // Informational only, no error.
            newline(&cp, cpe);
            csize = cpe - cp;
            snprintf(cp, csize, "BEPOCH is inconsistent with DATE-OBS");
          }
        }
      }
    }

    if (strncmp(orig_date, date, 72) != 0) {
      if (orig_date[0] == '\0') {
        newline(&cp, cpe);
        csize = cpe - cp;
        snprintf(cp, csize, "Set DATE%s to '%s' from MJD%s", dateid, date,
          dateid);
      } else {
        newline(&cp, cpe);
        csize = cpe - cp;
        snprintf(cp, csize, "Changed DATE%s from '%s' to '%s'", dateid,
          orig_date, date);
      }

      if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
    }
  }

  if (*infomsg) {
    wcserr_set(WCSERR_SET(FIXERR_DATE_FIX), infomsg);
  }

  return status;
}

//----------------------------------------------------------------------------

int obsfix(int ctrl, struct wcsprm *wcs)

{
  static const char *function = "obsfix";

  // IAU(1976) ellipsoid (as prescribed by WCS Paper VII).
  const double a  = 6378140.0;
  const double f  = 1.0 / 298.2577;
  const double e2 = (2.0 - f)*f;

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  // Set masks for checking partially-defined coordinate triplets.
  int havexyz = 7;
  havexyz -= 1*undefined(wcs->obsgeo[0]);
  havexyz -= 2*undefined(wcs->obsgeo[1]);
  havexyz -= 4*undefined(wcs->obsgeo[2]);
  int havelbh = 7;
  havelbh -= 1*undefined(wcs->obsgeo[3]);
  havelbh -= 2*undefined(wcs->obsgeo[4]);
  havelbh -= 4*undefined(wcs->obsgeo[5]);

  if (ctrl == 2) {
    // Make no changes.
    if (0 < havexyz && havexyz < 7) {
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Partially undefined Cartesian coordinate triplet");
    }

    if (0 < havelbh && havelbh < 7) {
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Partially undefined Geodetic coordinate triplet");
    }

    if (havexyz == 0 || havelbh == 0) {
      return FIXERR_NO_CHANGE;
    }
  }

  if (havexyz == 0 && havelbh == 0) {
    return FIXERR_NO_CHANGE;
  }


  char infomsg[256], *cp = infomsg, *cpe = infomsg + 256;
  infomsg[0] = '\0';
  size_t csize;

  int status = FIXERR_NO_CHANGE;

  size_t k;
  double x, y, z;
  if (havelbh == 7) {
    // Compute (x,y,z) from (lng,lat,hgt).
    double coslat, coslng, sinlat, sinlng;
    sincosd(wcs->obsgeo[3], &sinlng, &coslng);
    sincosd(wcs->obsgeo[4], &sinlat, &coslat);
    double n = a / sqrt(1.0 - e2*sinlat*sinlat);
    double rho = n + wcs->obsgeo[5];

    x = rho*coslng*coslat;
    y = rho*sinlng*coslat;
    z = (rho - n*e2)*sinlat;

    if (havexyz < 7) {
      // One or more of the Cartesian elements was undefined.
      status = FIXERR_SUCCESS;

      if (ctrl == 1 || !(havexyz & 1)) {
        wcs->obsgeo[0] = x;
        csize = cpe - cp;
        snprintf(cp, csize, "%s OBSGEO-X to %12.3f from OBSGEO-[LBH]",
          (havexyz & 1) ? "Reset" : "Set", x);
      }

      if (ctrl == 1 || !(havexyz & 2)) {
        wcs->obsgeo[1] = y;

        if ((k = strlen(cp))) {
          cp += k;
          strncpy(cp, ".\n", cpe - cp);
          cp += 2;
        }

        csize = cpe - cp;
        snprintf(cp, csize, "%s OBSGEO-Y to %12.3f from OBSGEO-[LBH]",
          (havexyz & 2) ? "Reset" : "Set", y);
      }

      if (ctrl == 1 || !(havexyz & 4)) {
        wcs->obsgeo[2] = z;
        if ((k = strlen(cp))) {
          cp += k;
          strncpy(cp, ".\n", cpe - cp);
          cp += 2;
        }

        csize = cpe - cp;
        snprintf(cp, csize, "%s OBSGEO-Z to %12.3f from OBSGEO-[LBH]",
          (havexyz & 4) ? "Reset" : "Set", z);
      }

      wcserr_set(WCSERR_SET(FIXERR_OBSGEO_FIX), infomsg);

      if (havexyz == 0) {
        // Skip the consistency check.
        return status;
      }
    }

  } else if (havexyz == 7) {
    // Compute (lng,lat,hgt) from (x,y,z).
    x = wcs->obsgeo[0];
    y = wcs->obsgeo[1];
    z = wcs->obsgeo[2];
    double r2 = x*x + y*y;

    // Iterate over the value of zeta.
    double coslat, coslng, sinlat, sinlng;
    double n, rho, zeta = z;
    for (int i = 0; i < 4; i++) {
      rho = sqrt(r2 + zeta*zeta);
      sinlat = zeta / rho;
      n = a / sqrt(1.0 - e2*sinlat*sinlat);

      zeta = z / (1.0 - n*e2/rho);
    }

    double lng = atan2d(y, x);
    double lat = asind(sinlat);
    double hgt = rho - n;

    if (havelbh < 7) {
      // One or more of the Geodetic elements was undefined.
      status = FIXERR_SUCCESS;

      if (ctrl == 1 || !(havelbh & 1)) {
        wcs->obsgeo[3] = lng;
        csize = cpe - cp;
        snprintf(cp, csize, "%s OBSGEO-L to %12.6f from OBSGEO-[XYZ]",
          (havelbh & 1) ? "Reset" : "Set", lng);
      }

      if (ctrl == 1 || !(havelbh & 2)) {
        wcs->obsgeo[4] = lat;
        if ((k = strlen(cp))) {
          cp += k;
          strncpy(cp, ".\n", cpe - cp);
          cp += 2;
        }

        csize = cpe - cp;
        snprintf(cp, csize, "%s OBSGEO-B to %12.6f from OBSGEO-[XYZ]",
          (havelbh & 2) ? "Reset" : "Set", lat);
      }

      if (ctrl == 1 || !(havelbh & 4)) {
        wcs->obsgeo[5] = hgt;
        if ((k = strlen(cp))) {
          cp += k;
          strncpy(cp, ".\n", cpe - cp);
          cp += 2;
        }

        csize = cpe - cp;
        snprintf(cp, csize, "%s OBSGEO-H to %12.3f from OBSGEO-[XYZ]",
          (havelbh & 4) ? "Reset" : "Set", hgt);
      }

      wcserr_set(WCSERR_SET(FIXERR_OBSGEO_FIX), infomsg);

      if (havelbh == 0) {
        // Skip the consistency check.
        return status;
      }
    }

    // Compute (x,y,z) from (lng,lat,hgt) for consistency checking.
    sincosd(wcs->obsgeo[3], &sinlng, &coslng);
    sincosd(wcs->obsgeo[4], &sinlat, &coslat);
    n = a / sqrt(1.0 - e2*sinlat*sinlat);
    rho = n + wcs->obsgeo[5];

    x = rho*coslng*coslat;
    y = rho*sinlng*coslat;
    z = (rho - n*e2)*sinlat;

  } else {
    return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
      "Observatory coordinates incomplete");
  }


  // Check consistency.
  double d, r2 = 0.0;
  d = wcs->obsgeo[0] - x;
  r2 += d*d;
  d = wcs->obsgeo[1] - y;
  r2 += d*d;
  d = wcs->obsgeo[2] - z;
  r2 += d*d;

  if (1.0 < r2) {
    d = sqrt(r2);
    return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
      "Observatory coordinates inconsistent by %.1f metres", d);
  }

  return status;
}


//----------------------------------------------------------------------------

int unitfix(int ctrl, struct wcsprm *wcs)

{
  const char *function = "unitfix";

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  int status = FIXERR_NO_CHANGE;

  char msg[512];
  strncpy(msg, "Changed units:", 512);

  for (int i = 0; i < wcs->naxis; i++) {
    char orig_unit[72];
    strncpy(orig_unit, wcs->cunit[i], 71);
    int result = wcsutrne(ctrl, wcs->cunit[i], &(wcs->err));
    if (result == 0 || result == 12) {
      size_t msglen = strlen(msg);
      if (msglen < 511) {
        wcsutil_null_fill(72, orig_unit);
        char msgtmp[192];
        snprintf(msgtmp, 192, "\n  '%s' -> '%s',", orig_unit, wcs->cunit[i]);
        strncpy(msg+msglen, msgtmp, 511-msglen);
        status = FIXERR_UNITS_ALIAS;
      }
    }
  }

  if (status == FIXERR_UNITS_ALIAS) {
    // Chop off the trailing ", ".
    size_t msglen = strlen(msg) - 2;
    msg[msglen] = '\0';
    wcserr_set(WCSERR_SET(FIXERR_UNITS_ALIAS), msg);

    status = FIXERR_SUCCESS;
  }

  return status;
}

//----------------------------------------------------------------------------

int spcfix(struct wcsprm *wcs)

{
  static const char *function = "spcfix";

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  for (int i = 0; i < wcs->naxis; i++) {
    // Translate an AIPS-convention spectral type if present.
    char ctype[9], specsys[9];
    int status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys);
    if (status == FIXERR_SUCCESS) {
      // An AIPS type was found but it may match what we already have.
      status = FIXERR_NO_CHANGE;

      // Was specsys translated?
      if (wcs->specsys[0] == '\0' && *specsys) {
        strncpy(wcs->specsys, specsys, 9);
        wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
          "Changed SPECSYS to '%s'", specsys);
        status = FIXERR_SUCCESS;
      }

      // Was ctype translated?  Have to null-fill for comparing them.
      wcsutil_null_fill(9, wcs->ctype[i]);
      if (strncmp(wcs->ctype[i], ctype, 9) != 0) {
        // ctype was translated...
        if (status == FIXERR_SUCCESS) {
          // ...and specsys was also.
          wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
            "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s' "
            "(VELREF=%d)", i+1, wcs->ctype[i], ctype, wcs->specsys,
            wcs->velref);
        } else {
          wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
            "Changed CTYPE%d from '%s' to '%s' (VELREF=%d)", i+1,
            wcs->ctype[i], ctype, wcs->velref);
          status = FIXERR_SUCCESS;
        }

        strncpy(wcs->ctype[i], ctype, 9);
      }

      // Tidy up.
      if (status == FIXERR_SUCCESS) {
        wcsutil_null_fill(72, wcs->ctype[i]);
        wcsutil_null_fill(72, wcs->specsys);
      }

      // No need to check for others, wcsset() will fail if so.
      return status;

    } else if (status == SPCERR_BAD_SPEC_PARAMS) {
      // An AIPS spectral type was found but with invalid velref.
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Invalid parameter value: velref = %d", wcs->velref);
    }
  }

  return FIXERR_NO_CHANGE;
}

//----------------------------------------------------------------------------

int celfix(struct wcsprm *wcs)

{
  static const char *function = "celfix";

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  // Initialize if required.
  int status;
  if (abs(wcs->flag) != WCSSET) {
    if ((status = wcsset(wcs))) return fix_wcserr[status];
  }

  // Was an NCP or GLS projection code translated?
  if (wcs->lat >= 0) {
    // Check ctype.
    if (strcmp(wcs->ctype[wcs->lat]+5, "NCP") == 0) {
      strncpy(wcs->ctype[wcs->lng]+5, "SIN", 4);
      strncpy(wcs->ctype[wcs->lat]+5, "SIN", 4);

      if (wcs->npvmax < wcs->npv + 2) {
        // Allocate space for two more PVi_ma keyvalues.
        if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) {
          if (!(wcs->pv = calloc(wcs->npv+2, sizeof(struct pvcard)))) {
            wcs->pv = wcs->m_pv;
            return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
          }

          wcs->npvmax = wcs->npv + 2;
          wcs->m_flag = WCSSET;

          for (int k = 0; k < wcs->npv; k++) {
            wcs->pv[k] = wcs->m_pv[k];
          }

          if (wcs->m_pv) free(wcs->m_pv);
          wcs->m_pv = wcs->pv;

        } else {
          return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
        }
      }

      struct celprm *wcscel = &(wcs->cel);
      struct prjprm *wcsprj = &(wcscel->prj);
      wcs->pv[wcs->npv].i = wcs->lat + 1;
      wcs->pv[wcs->npv].m = 1;
      wcs->pv[wcs->npv].value = wcsprj->pv[1];
      (wcs->npv)++;

      wcs->pv[wcs->npv].i = wcs->lat + 1;
      wcs->pv[wcs->npv].m = 2;
      wcs->pv[wcs->npv].value = wcsprj->pv[2];
      (wcs->npv)++;

      return FIXERR_SUCCESS;

    } else if (strcmp(wcs->ctype[wcs->lat]+5, "GLS") == 0) {
      strncpy(wcs->ctype[wcs->lng]+5, "SFL", 4);
      strncpy(wcs->ctype[wcs->lat]+5, "SFL", 4);

      if (wcs->crval[wcs->lng] != 0.0 || wcs->crval[wcs->lat] != 0.0) {
        // In the AIPS convention, setting the reference longitude and
        // latitude for GLS does not create an oblique graticule.  A non-zero
        // reference longitude introduces an offset in longitude in the normal
        // way, whereas a non-zero reference latitude simply translates the
        // reference point (i.e. the map as a whole) to that latitude.  This
        // might be effected by adjusting CRPIXja but that is complicated by
        // the linear transformation and instead is accomplished here by
        // setting theta_0.
        if (wcs->npvmax < wcs->npv + 3) {
          // Allocate space for three more PVi_ma keyvalues.
          if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) {
            if (!(wcs->pv = calloc(wcs->npv+3, sizeof(struct pvcard)))) {
              wcs->pv = wcs->m_pv;
              return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
            }

            wcs->npvmax = wcs->npv + 3;
            wcs->m_flag = WCSSET;

            for (int k = 0; k < wcs->npv; k++) {
              wcs->pv[k] = wcs->m_pv[k];
            }

            if (wcs->m_pv) free(wcs->m_pv);
            wcs->m_pv = wcs->pv;

          } else {
            return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
          }
        }

        wcs->pv[wcs->npv].i = wcs->lng + 1;
        wcs->pv[wcs->npv].m = 0;
        wcs->pv[wcs->npv].value = 1.0;
        (wcs->npv)++;

        // Note that the reference longitude is still zero.
        wcs->pv[wcs->npv].i = wcs->lng + 1;
        wcs->pv[wcs->npv].m = 1;
        wcs->pv[wcs->npv].value = 0.0;
        (wcs->npv)++;

        wcs->pv[wcs->npv].i = wcs->lng + 1;
        wcs->pv[wcs->npv].m = 2;
        wcs->pv[wcs->npv].value = wcs->crval[wcs->lat];
        (wcs->npv)++;
      }

      return FIXERR_SUCCESS;
    }
  }

  return FIXERR_NO_CHANGE;
}

//----------------------------------------------------------------------------

int cylfix(const int naxis[], struct wcsprm *wcs)

{
  static const char *function = "cylfix";

  if (naxis == 0x0) return FIXERR_NO_CHANGE;
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  // Initialize if required.
  int status;
  if (abs(wcs->flag) != WCSSET) {
    if ((status = wcsset(wcs))) return fix_wcserr[status];
  }

  // Check that we have a cylindrical projection.
  if (wcs->cel.prj.category != CYLINDRICAL) return FIXERR_NO_CHANGE;
  if (wcs->naxis < 2) return FIXERR_NO_CHANGE;


  // Compute the native longitude in each corner of the image.
  unsigned short ncnr = 1 << wcs->naxis;

  unsigned short indx[NAXMAX];
  for (int k = 0; k < NAXMAX; k++) {
    indx[k] = 1 << k;
  }

  int    stat[4];
  double img[4][NAXMAX], phi[4], pix[4][NAXMAX], theta[4], world[4][NAXMAX];

  double phimin =  1.0e99;
  double phimax = -1.0e99;
  for (unsigned short icnr = 0; icnr < ncnr;) {
    // Do four corners at a time.
    for (int j = 0; j < 4; j++, icnr++) {
      double *pixj = pix[j];

      for (int k = 0; k < wcs->naxis; k++) {
        if (icnr & indx[k]) {
          *(pixj++) = naxis[k] + 0.5;
        } else {
          *(pixj++) = 0.5;
        }
      }
    }

    if (!(status = wcsp2s(wcs, 4, NAXMAX, pix[0], img[0], phi, theta,
                          world[0], stat))) {
      for (int j = 0; j < 4; j++) {
        if (phi[j] < phimin) phimin = phi[j];
        if (phi[j] > phimax) phimax = phi[j];
      }
    }
  }

  if (phimin > phimax) return fix_wcserr[status];

  // Any changes needed?
  if (phimin >= -180.0 && phimax <= 180.0) return FIXERR_NO_CHANGE;


  // Compute the new reference pixel coordinates.
  double phi0 = (phimin + phimax) / 2.0;
  double theta0 = 0.0;

  double x, y;
  if ((status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y,
                       stat))) {
    if (status == PRJERR_BAD_PARAM) {
      status = FIXERR_BAD_PARAM;
    } else {
      status = FIXERR_NO_REF_PIX_COORD;
    }
    return wcserr_set(WCSFIX_ERRMSG(status));
  }

  for (int k = 0; k < wcs->naxis; k++) {
    img[0][k] = 0.0;
  }
  img[0][wcs->lng] = x;
  img[0][wcs->lat] = y;

  if ((status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0]))) {
    return wcserr_set(WCSFIX_ERRMSG(fix_linerr[status]));
  }


  // Compute celestial coordinates at the new reference pixel.
  if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0],
                       stat))) {
    return fix_wcserr[status];
  }

  // Compute native coordinates of the celestial pole.
  double lng =  0.0;
  double lat = 90.0;
  (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng, &lat, phi, theta);

  wcs->crpix[wcs->lng] = pix[0][wcs->lng];
  wcs->crpix[wcs->lat] = pix[0][wcs->lat];
  wcs->crval[wcs->lng] = world[0][wcs->lng];
  wcs->crval[wcs->lat] = world[0][wcs->lat];
  wcs->lonpole = phi[0] - phi0;

  wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0;
  return wcsset(wcs);
}

//----------------------------------------------------------------------------

// Helper function used only by wcspcx().
static int unscramble(int n, int mapto[], int step, int type, void *vptr);

int wcspcx(
  struct wcsprm *wcs,
  int dopc,
  int permute,
  double rotn[2])

{
  static const char *function = "wcspcx";

  // Initialize if required.
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  int status;
  if (abs(wcs->flag) != WCSSET) {
    if ((status = wcsset(wcs))) return fix_wcserr[status];
  }

  // Check for CDi_j usage.
  double *wcscd = wcs->cd;
  if ((wcs->altlin & 1) || !(wcs->altlin & 2)) {
    if ((wcs->altlin & 1) && dopc == 1) {
      // Recompose PCi_j + CDELTi.
      wcscd = wcs->pc;
    } else {
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "CDi_j is not used in this coordinate representation");
    }
  }

  // Check for sequent distortions.
  if (wcs->lin.disseq) {
    return wcserr_set(WCSERR_SET(FIXERR_BAD_COORD_TRANS),
      "Cannot handle coordinate descriptions containing sequent distortions");
  }


  // Allocate memory in bulk for two nxn matrices.
  int naxis = wcs->naxis;
  size_t naxiz = naxis;
  double *mem;
  if ((mem = calloc(2*naxiz*naxiz, sizeof(double))) == 0x0) {
    return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
  }

  double *mat = mem;
  double *inv = mem + naxiz*naxiz;

  // Construct the transpose of CDi_j with each element squared.
  double *matij = mat;
  for (int i = 0; i < naxis; i++) {
    double *cdji = wcscd + i;
    for (int j = 0; j < naxis; j++) {
      *(matij++) = (*cdji) * (*cdji);
      cdji += naxis;
    }
  }

  // Invert the matrix.
  if (matinv(naxis, mat, inv)) {
    free(mem);
    return wcserr_set(WCSERR_SET(FIXERR_SINGULAR_MTX),
      "No solution for CDi_j matrix decomposition");
  }

  // Apply scaling.
  double *invij = inv;
  double *pcij = wcs->pc;
  double *cdij = wcscd;
  for (int i = 0; i < naxis; i++) {
    double scl = 0.0;
    for (int j = 0; j < naxis; j++) {
      scl += *(invij++);
    }

    scl = sqrt(scl);
    wcs->cdelt[i] /= scl;

    for (int j = 0; j < naxis; j++) {
      *(pcij++) = *(cdij++) * scl;
    }
  }

  // mapto[i] records where row i of PCi_j should move to.
  int *mapto = 0x0;
  if ((mapto = (int*)malloc(naxis*sizeof(int))) == 0x0) {
    free(mem);
    return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
  }

  for (int i = 0; i < naxis; i++) {
    mapto[i] = -1;
  }

  // Ensure that latitude always follows longitude.
  if (wcs->lng >= 0 && wcs->lat >= 0) {
    double *pci = wcs->pc + naxiz*wcs->lng;

    // Take the first non-zero element in the row.
    for (int j = 0; j < naxis; j++) {
      if (fabs(pci[j]) != 0.0) {
        mapto[wcs->lng] = j;
        break;
      }
    }

    if (mapto[wcs->lng] == naxis-1) {
      mapto[wcs->lng]--;
    }

    mapto[wcs->lat] = mapto[wcs->lng] + 1;
  }

  // Fill in the rest of the row permutation map.
  for (int j = 0; j < naxis; j++) {
    // Column j.
    double *pcij = wcs->pc + j;
    double colmax = 0.0;

    // Look down the column to find the absolute maximum element.
    for (int i = 0; i < naxis; i++, pcij += naxis) {
      if (!(mapto[i] < 0)) {
        // This row is already mapped.
        continue;
      }

      if (fabs(*pcij) > colmax) {
        mapto[i] = j;
        colmax = fabs(*pcij);
      }
    }
  }

  // Check validity of the permutation map.
  int badmap = 0;
  for (int j = 0; j < naxis; j++) {
    int i;
    for (i = 0; i < naxis; i++) {
      if (mapto[i] == j) break;
    }

    if (i == naxis) {
      badmap = 1;
      break;
    }
  }

  if (badmap) {
    // Bail out.
    for (int i = 0; i < naxis; i++) {
      mapto[i] = i;
    }
  }


  // Fix the sign of CDELTi.  Celestial axes are special, otherwise diagonal
  // elements of the correctly permuted matrix should be positive.
  for (int i = 0; i < naxis; i++) {
    int chsgn;
    double *pci = wcs->pc + naxiz*i;

    // Celestial axes are special.
    if (i == wcs->lng) {
      // Longitude axis - force CDELTi < 0.0.
      chsgn = (wcs->cdelt[i] > 0.0);
    } else if (i == wcs->lat) {
      // Latitude axis - force CDELTi > 0.0.
      chsgn = (wcs->cdelt[i] < 0.0);
    } else {
      chsgn = (pci[mapto[i]] < 0.0);
    }

    if (chsgn) {
      wcs->cdelt[i] = -wcs->cdelt[i];

      for (int j = 0; j < naxis; j++) {
        // Test needed to prevent negative zeros.
        if (pci[j] != 0.0) {
          pci[j] = -pci[j];
        }
      }
    }
  }

  free(mem);

  // Setting bit 3 in wcsprm::altlin stops wcsset() from reconstructing
  // PCi_j and CDELTi from CDi_j.
  wcs->altlin |= 8;


  // Compute rotation angle of each basis vector of the celestial axes.
  if (rotn) {
    if (wcs->lng < 0 || wcs->lat < 0) {
      // No celestial axes.
      rotn[0] = 0.0;
      rotn[1] = 0.0;

    } else {
      double x, y;
      x =  wcs->pc[naxis*wcs->lng + mapto[wcs->lng]];
      y =  wcs->pc[naxis*wcs->lat + mapto[wcs->lng]];
      rotn[0] = atan2d(y, x);

      y = -wcs->pc[naxis*wcs->lng + mapto[wcs->lat]];
      x =  wcs->pc[naxis*wcs->lat + mapto[wcs->lat]];
      rotn[1] = atan2d(y, x);
    }
  }


  // Permute rows?
  if (permute) {
    // Check whether there's anything to unscramble.
    int scrambled = 0;
    for (int i = 0; i < naxis; i++) {
      if (mapto[i] != i) {
        scrambled = 1;
        break;
      }
    }

    if (scrambled) {
      for (int i = 0; i < naxis; i++) {
        // Do columns of the PCi_ja matrix.
        if (unscramble(naxis, mapto, naxis, 1, wcs->pc + i)) goto cleanup;
      }
      if (unscramble(naxis, mapto, 1, 1, wcs->cdelt)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->crval)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 2, wcs->cunit)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 2, wcs->ctype)) goto cleanup;

      for (int ipv = 0; ipv < wcs->npv; ipv++) {
        // Noting that PVi_ma axis numbers are 1-relative.
        int i = wcs->pv[ipv].i - 1;
        wcs->pv[ipv].i = mapto[i] + 1;
      }

      for (int ips = 0; ips < wcs->nps; ips++) {
        // Noting that PSi_ma axis numbers are 1-relative.
        int i = wcs->ps[ips].i - 1;
        wcs->ps[ips].i = mapto[i] + 1;
      }

      if (wcs->altlin & 2) {
        for (int i = 0; i < naxis; i++) {
          // Do columns of the CDi_ja matrix.
          if (unscramble(naxis, mapto, naxis, 1, wcs->cd + i)) goto cleanup;
        }
      }

      if (wcs->altlin & 4) {
        if (unscramble(naxis, mapto, 1, 1, wcs->crota)) goto cleanup;
      }

      if (unscramble(naxis, mapto, 1, 3, wcs->colax)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 2, wcs->cname)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->crder)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->csyer)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->czphs)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->cperi)) goto cleanup;

      // Coordinate lookup tables.
      for (int itab = 0; itab < wcs->ntab; itab++) {
        for (int m = 0; m < wcs->tab[itab].M; m++) {
          int i = wcs->tab[itab].map[m];
          wcs->tab[itab].map[m] = mapto[i];
        }
      }

      for (int iwtb = 0; iwtb < wcs->nwtb; iwtb++) {
        int i = wcs->wtb[iwtb].i;
        wcs->wtb[iwtb].i = mapto[i];
      }

      // Distortions?  No.  Prior distortions operate on pixel coordinates and
      // therefore are not permuted, and sequent distortions are not handled.
    }
  }

  free(mapto);

  // Reset the struct.
  wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0;
  if ((status = wcsset(wcs))) return fix_wcserr[status];

  return FIXERR_SUCCESS;

cleanup:
  if (mapto) free(mapto);
  return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
}


int unscramble(
  int n,
  int mapto[],
  int step,
  int type,
  void *vptr)

{
  if (step == 0) step = 1;

  if (type == 1) {
    double *dval = (double *)vptr;
    double *dtmp;
    if ((dtmp = (double *)malloc(n*sizeof(double))) == 0x0) {
      return 1;
    }

    for (int i = 0, j = 0; i < n; i++, j += step) {
      int k = mapto[i];
      dtmp[k] = dval[j];
    }

    for (int i = 0, j = 0; i < n; i++, j += step) {
      dval[j] = dtmp[i];
    }

    free(dtmp);

  } else if (type == 2) {
    char (*cval)[72] = (char (*)[72])vptr;
    char (*ctmp)[72];
    if ((ctmp = (char (*)[72])malloc(n*sizeof(char[72]))) == 0x0) {
      return 1;
    }

    for (int i = 0; i < n; i++) {
      int k = mapto[i];
      memcpy(ctmp[k], cval[i], 72);
    }

    for (int i = 0; i < n; i++) {
      memcpy(cval[i], ctmp[i], 72);
    }

    free(ctmp);

  } else if (type == 3) {
    int *ival = (int *)vptr;
    int *itmp;
    if ((itmp = (int *)malloc(n*sizeof(int))) == 0x0) {
      return 1;
    }

    for (int i = 0; i < n; i++) {
      int k = mapto[i];
      itmp[k] = ival[i];
    }

    for (int i = 0; i < n; i++) {
      ival[i] = itmp[i];
    }

    free(itmp);
  }

  return 0;
}
@


8.6
log
@WCSLIB 8.6
@
text
@d2 1
a2 1
  WCSLIB 8.6 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 8.5.1.2 2026/03/29 13:36:16 mcalabre Exp mcalabre $
d36 1
a42 3
// Maximum number of coordinate axes that can be handled.
#define NMAX 16

d230 1
a230 1
    double *cd = wcs->cd + i*naxis;
d241 1
a241 1
    cd = wcs->cd + i * (naxis + 1);
d266 1
a266 1
static void write_date(char *buf, int hour, int minute, double sec)
d270 2
a271 2
  wcsutil_double2str(ctmp, "%04.1f", sec);
  sprintf(buf, "T%.2d:%.2d:%s", hour, minute, ctmp);
d275 1
a275 1
static char *newline(char **cp)
d278 2
a279 2
  size_t k;
  if ((k = strlen(*cp))) {
d281 1
a281 1
    strcat(*cp, ".\n");
d308 3
a310 3
  char infomsg[512];
  char *cp = infomsg;
  *cp = '\0';
d345 1
d354 3
a356 1
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from JEPOCH", *wcsmjd);
d361 3
a363 1
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from BEPOCH", *wcsmjd);
d398 1
a398 1
        sprintf(date, "%.4d-%.2d-%.2d", year, month, day);
d414 1
a414 1
          sprintf(date+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);
d418 1
a418 1
            sprintf(date+19, ".%.3d", msec%1000);
d427 3
a429 1
        sprintf(newline(&cp), "Invalid DATE%s format '%s' is too short",
d439 3
a441 1
          sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date);
d448 3
a450 1
            sprintf(newline(&cp), "Invalid time in DATE%s '%s'", dateid,
d469 3
a471 1
          sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date);
d478 3
a480 1
            sprintf(newline(&cp), "Invalid time in DATE%s '%s'", dateid,
d504 3
a506 1
            sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid,
d515 3
a517 1
            sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid,
d525 3
a527 1
          sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date);
d534 1
a534 1
        sprintf(date, "%.4d-%.2d-%.2d", year, month, day);
d537 1
a537 1
      // Compute MJD.
d539 1
a539 1
      mjd[0] = (double)((1461*(year - (12-month)/10 + 4712))/4
d542 2
a543 1
               + day - 2399904);
d554 3
a556 1
        sprintf(newline(&cp), "Set MJD%s to %.6f from DATE%s", dateid,
d572 3
a574 1
          sprintf(newline(&cp),
d587 3
a589 1
            sprintf(newline(&cp), "JEPOCH is inconsistent with DATE-OBS");
d599 3
a601 1
            sprintf(newline(&cp), "BEPOCH is inconsistent with DATE-OBS");
d607 1
a607 1
    if (strncmp(orig_date, date, 72)) {
d609 3
a611 1
        sprintf(newline(&cp), "Set DATE%s to '%s' from MJD%s", dateid, date,
d614 3
a616 1
        sprintf(newline(&cp), "Changed DATE%s from '%s' to '%s'", dateid,
d678 1
a678 1
  char infomsg[256];
d680 1
a700 1
      char *cp = infomsg;
d704 2
a705 1
        sprintf(cp, "%s OBSGEO-X to %12.3f from OBSGEO-[LBH]",
d713 3
a715 2
          strcat(cp+k, ".\n");
          cp += k + 2;
d718 2
a719 1
        sprintf(cp, "%s OBSGEO-Y to %12.3f from OBSGEO-[LBH]",
d726 3
a728 2
          strcat(cp+k, ".\n");
          cp += k + 2;
d731 2
a732 1
        sprintf(cp, "%s OBSGEO-Z to %12.3f from OBSGEO-[LBH]",
a768 1
      char *cp = infomsg;
d772 2
a773 1
        sprintf(cp, "%s OBSGEO-L to %12.6f from OBSGEO-[XYZ]",
d780 3
a782 2
          strcat(cp+k, ".\n");
          cp += k + 2;
d785 2
a786 1
        sprintf(cp, "%s OBSGEO-B to %12.6f from OBSGEO-[XYZ]",
d793 3
a795 2
          strcat(cp+k, ".\n");
          cp += k + 2;
d798 2
a799 1
        sprintf(cp, "%s OBSGEO-H to %12.3f from OBSGEO-[XYZ]",
d870 1
a870 1
        sprintf(msgtmp, "\n  '%s' -> '%s',", orig_unit, wcs->cunit[i]);
d917 1
a917 1
      if (strncmp(wcs->ctype[i], ctype, 9)) {
d974 2
a975 2
      strcpy(wcs->ctype[wcs->lng]+5, "SIN");
      strcpy(wcs->ctype[wcs->lat]+5, "SIN");
d1015 2
a1016 2
      strcpy(wcs->ctype[wcs->lng]+5, "SFL");
      strcpy(wcs->ctype[wcs->lat]+5, "SFL");
d1099 2
a1100 2
  unsigned short indx[NMAX];
  for (int k = 0; k < NMAX; k++) {
d1105 1
a1105 1
  double img[4][NMAX], phi[4], pix[4][NMAX], theta[4], world[4][NMAX];
d1123 2
a1124 2
    if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0],
                          stat))) {
d1229 1
d1231 1
a1231 1
  if ((mem = calloc(2*naxis*naxis, sizeof(double))) == 0x0) {
d1236 1
a1236 1
  double *inv = mem + naxis*naxis;
d1249 2
a1250 1
  if ((status = matinv(naxis, mat, inv))) {
d1286 1
a1286 1
    double *pci = wcs->pc + naxis*wcs->lng;
d1323 21
d1349 1
a1349 1
    double *pci = wcs->pc + naxis*i;
d1501 3
a1503 2
    for (int i = 0; i < n; i++) {
      dtmp[mapto[i]] = dval[i*step];
d1506 2
a1507 2
    for (int i = 0; i < n; i++) {
      dval[i*step] = dtmp[i];
d1515 1
a1515 1
    if ((ctmp = (char (*)[72])malloc(n*72*sizeof(char))) == 0x0) {
d1520 2
a1521 1
      memcpy(ctmp[mapto[i]], cval[i], 72);
d1538 2
a1539 1
      itmp[mapto[i]] = ival[i];
@


8.6.1.1
log
@WCSLIB 8.6 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $
@


8.6.1.2
log
@Change prompted by Claude Code:
 - Use NAXMAX from wcslimits.h rather than setting a local value (NMAX).
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6.1.1 2026/03/29 14:19:26 mcalabre Exp mcalabre $
a35 1
#include "wcslimits.h"
d42 3
d1058 2
a1059 2
  unsigned short indx[NAXMAX];
  for (int k = 0; k < NAXMAX; k++) {
d1064 1
a1064 1
  double img[4][NAXMAX], phi[4], pix[4][NAXMAX], theta[4], world[4][NAXMAX];
d1082 2
a1083 2
    if (!(status = wcsp2s(wcs, 4, NAXMAX, pix[0], img[0], phi, theta,
                          world[0], stat))) {
@


8.6.1.3
log
@Inconsequential changes prompted by clang-tidy:
 - Replaced use of strcpy() with strncpy(), and sprintf() with snprintf()
   (clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling).
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6.1.2 2026/04/05 08:35:50 mcalabre Exp mcalabre $
d266 1
a266 1
static void write_date(char buf[32], int hour, int minute, double sec)
d271 1
a271 1
  snprintf(buf, 32, "T%.2d:%.2d:%.5s", hour, minute, ctmp);
d275 1
a275 1
static char *newline(char **cp, size_t csize)
d278 2
a279 2
  size_t k = strlen(*cp);
  if (k && k < csize) {
d353 1
a353 3
          newline(&cp, 512);
          size_t csize = (infomsg + 512) - cp;
          snprintf(cp, csize, "Set MJD-OBS to %.6f from JEPOCH", *wcsmjd);
d358 1
a358 3
          newline(&cp, 512);
          size_t csize = (infomsg + 512) - cp;
          snprintf(cp, csize, "Set MJD-OBS to %.6f from BEPOCH", *wcsmjd);
d393 1
a393 1
        snprintf(date, 72, "%.4d-%.2d-%.2d", year, month, day);
d409 1
a409 1
          snprintf(date+10, 62, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);
d413 1
a413 1
            snprintf(date+19, 53, ".%.3d", msec%1000);
d422 1
a422 3
        newline(&cp, 512);
        size_t csize = (infomsg + 512) - cp;
        snprintf(cp, csize, "Invalid DATE%s format '%s' is too short",
d432 1
a432 3
          newline(&cp, 512);
          size_t csize = (infomsg + 512) - cp;
          snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid, date);
d439 1
a439 3
            newline(&cp, 512);
            size_t csize = (infomsg + 512) - cp;
            snprintf(cp, csize, "Invalid time in DATE%s '%s'", dateid,
d458 1
a458 3
          newline(&cp, 512);
          size_t csize = (infomsg + 512) - cp;
          snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid, date);
d465 1
a465 3
            newline(&cp, 512);
            size_t csize = (infomsg + 512) - cp;
            snprintf(cp, csize, "Invalid time in DATE%s '%s'", dateid,
d489 1
a489 3
            newline(&cp, 512);
            size_t csize = (infomsg + 512) - cp;
            snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid,
d498 1
a498 3
            newline(&cp, 512);
            size_t csize = (infomsg + 512) - cp;
            snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid,
d506 1
a506 3
          newline(&cp, 512);
          size_t csize = (infomsg + 512) - cp;
          snprintf(cp, csize, "Invalid DATE%s format '%s'", dateid, date);
d513 1
a513 1
        snprintf(date, 72, "%.4d-%.2d-%.2d", year, month, day);
d532 1
a532 3
        newline(&cp, 512);
        size_t csize = (infomsg + 512) - cp;
        snprintf(cp, csize, "Set MJD%s to %.6f from DATE%s", dateid,
d548 1
a548 3
          newline(&cp, 512);
          size_t csize = (infomsg + 512) - cp;
          snprintf(cp, csize,
d561 1
a561 3
            newline(&cp, 512);
            size_t csize = (infomsg + 512) - cp;
            snprintf(cp, csize, "JEPOCH is inconsistent with DATE-OBS");
d571 1
a571 3
            newline(&cp, 512);
            size_t csize = (infomsg + 512) - cp;
            snprintf(cp, csize, "BEPOCH is inconsistent with DATE-OBS");
d579 1
a579 3
        newline(&cp, 512);
        size_t csize = (infomsg + 512) - cp;
        snprintf(cp, csize, "Set DATE%s to '%s' from MJD%s", dateid, date,
d582 1
a582 3
        newline(&cp, 512);
        size_t csize = (infomsg + 512) - cp;
        snprintf(cp, csize, "Changed DATE%s from '%s' to '%s'", dateid,
d670 1
a670 2
        size_t csize = (infomsg + 256) - cp;
        snprintf(cp, csize, "%s OBSGEO-X to %12.3f from OBSGEO-[LBH]",
d682 1
a682 2
        size_t csize = (infomsg + 256) - cp;
        snprintf(cp, csize, "%s OBSGEO-Y to %12.3f from OBSGEO-[LBH]",
d693 1
a693 2
        size_t csize = (infomsg + 256) - cp;
        snprintf(cp, csize, "%s OBSGEO-Z to %12.3f from OBSGEO-[LBH]",
d734 1
a734 2
        size_t csize = (infomsg + 256) - cp;
        snprintf(cp, csize, "%s OBSGEO-L to %12.6f from OBSGEO-[XYZ]",
d745 1
a745 2
        size_t csize = (infomsg + 256) - cp;
        snprintf(cp, csize, "%s OBSGEO-B to %12.6f from OBSGEO-[XYZ]",
d756 1
a756 2
        size_t csize = (infomsg + 256) - cp;
        snprintf(cp, csize, "%s OBSGEO-H to %12.3f from OBSGEO-[XYZ]",
d827 1
a827 1
        snprintf(msgtmp, 192, "\n  '%s' -> '%s',", orig_unit, wcs->cunit[i]);
d931 2
a932 2
      strncpy(wcs->ctype[wcs->lng]+5, "SIN", 4);
      strncpy(wcs->ctype[wcs->lat]+5, "SIN", 4);
d972 2
a973 2
      strncpy(wcs->ctype[wcs->lng]+5, "SFL", 4);
      strncpy(wcs->ctype[wcs->lat]+5, "SFL", 4);
@


8.6.1.4
log
@Argument list to wcsutil_double2str() changed to pass the buffer size.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6.1.3 2026/04/10 09:03:47 mcalabre Exp mcalabre $
d270 1
a270 1
  wcsutil_double2str(ctmp, 32, "%04.1f", sec);
@


8.6.1.5
log
@Found by clang-tidy:
 - Ensure that allocated memory is free'd on error return from wcspcx()
   (clang-analyzer-unix.Malloc).
 - Fixed sizeof() type in a call to malloc() in unscramble()
   (clang-analyzer-unix.MallocSizeof).
Inconsequential changes prompted by clang-tidy:
 - Use size_t type for address calculations
   (bugprone-implicit-widening-of-multiplication-result).
 - Quelled a mistaken warning in datfix() concerning integer division
   (bugprone-integer-division).
 - Explicitly test the return value of strncmp() in if-statements
   (bugprone-suspicious-string-compare).
 - Replaced use of strcat() with strncat()
   (clang-analyzer-security.insecureAPI.strcpy).
 - Added some balm to unscramble() to thwart a mistaken warning
   (clang-analyzer-core.uninitialized.Assign).
 - Removed an unused variable assignment from wcspcx()
   (clang-analyzer-deadcode.DeadStores).
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6.1.4 2026/04/10 09:23:11 mcalabre Exp mcalabre $
d230 1
a230 1
    double *cd = wcs->cd + (size_t)(i*naxis);
d241 1
a241 1
    cd = wcs->cd + (size_t)(i*(naxis + 1));
d275 1
a275 1
static char *newline(char **cp, char *cpe)
d279 1
a279 1
  if (k) {
d281 1
a281 1
    strncat(*cp, ".\n", cpe - *cp);
d308 3
a310 3
  char infomsg[512], *cp = infomsg, *cpe = infomsg + 512;
  infomsg[0] = '\0';
  size_t csize;
d353 2
a354 2
          newline(&cp, cpe);
          csize = cpe - cp;
d360 2
a361 2
          newline(&cp, cpe);
          csize = cpe - cp;
d426 2
a427 2
        newline(&cp, cpe);
        csize = cpe - cp;
d438 2
a439 2
          newline(&cp, cpe);
          csize = cpe - cp;
d447 2
a448 2
            newline(&cp, cpe);
            csize = cpe - cp;
d468 2
a469 2
          newline(&cp, cpe);
          csize = cpe - cp;
d477 2
a478 2
            newline(&cp, cpe);
            csize = cpe - cp;
d503 2
a504 2
            newline(&cp, cpe);
            csize = cpe - cp;
d514 2
a515 2
            newline(&cp, cpe);
            csize = cpe - cp;
d524 2
a525 2
          newline(&cp, cpe);
          csize = cpe - cp;
d536 1
a536 1
      // Compute MJD using Hatcher's algorithm, based on integer division.
d538 1
a538 1
      int m = (1461*(year - (12-month)/10 + 4712))/4
d541 1
a541 2
               + day - 2399904;
      mjd[0] = (double)m;
d552 2
a553 2
        newline(&cp, cpe);
        csize = cpe - cp;
d570 2
a571 2
          newline(&cp, cpe);
          csize = cpe - cp;
d585 2
a586 2
            newline(&cp, cpe);
            csize = cpe - cp;
d597 2
a598 2
            newline(&cp, cpe);
            csize = cpe - cp;
d605 1
a605 1
    if (strncmp(orig_date, date, 72) != 0) {
d607 2
a608 2
        newline(&cp, cpe);
        csize = cpe - cp;
d612 2
a613 2
        newline(&cp, cpe);
        csize = cpe - cp;
d676 1
a676 1
  char infomsg[256], *cp = infomsg, *cpe = infomsg + 256;
a677 1
  size_t csize;
d698 1
d702 1
a702 1
        csize = cpe - cp;
d711 2
a712 3
          cp += k;
          strncat(cp, ".\n", cpe - cp);
          cp += 2;
d715 1
a715 1
        csize = cpe - cp;
d723 2
a724 3
          cp += k;
          strncat(cp, ".\n", cpe - cp);
          cp += 2;
d727 1
a727 1
        csize = cpe - cp;
d765 1
d769 1
a769 1
        csize = cpe - cp;
d777 2
a778 3
          cp += k;
          strncat(cp, ".\n", cpe - cp);
          cp += 2;
d781 1
a781 1
        csize = cpe - cp;
d789 2
a790 3
          cp += k;
          strncat(cp, ".\n", cpe - cp);
          cp += 2;
d793 1
a793 1
        csize = cpe - cp;
d912 1
a912 1
      if (strncmp(wcs->ctype[i], ctype, 9) != 0) {
a1223 1
  size_t naxiz = naxis;
d1225 1
a1225 1
  if ((mem = calloc(2*naxiz*naxiz, sizeof(double))) == 0x0) {
d1230 1
a1230 1
  double *inv = mem + naxiz*naxiz;
d1243 1
a1243 2
  if (matinv(naxis, mat, inv)) {
    free(mem);
d1279 1
a1279 1
    double *pci = wcs->pc + naxiz*wcs->lng;
a1315 21
  // Check validity of the permutation map.
  int badmap = 0;
  for (int j = 0; j < naxis; j++) {
    int i;
    for (i = 0; i < naxis; i++) {
      if (mapto[i] == j) break;
    }

    if (i == naxis) {
      badmap = 1;
      break;
    }
  }

  if (badmap) {
    // Bail out.
    for (int i = 0; i < naxis; i++) {
      mapto[i] = i;
    }
  }

d1321 1
a1321 1
    double *pci = wcs->pc + naxiz*i;
d1473 2
a1474 3
    for (int i = 0, j = 0; i < n; i++, j += step) {
      int k = mapto[i];
      dtmp[k] = dval[j];
d1477 2
a1478 2
    for (int i = 0, j = 0; i < n; i++, j += step) {
      dval[j] = dtmp[i];
d1486 1
a1486 1
    if ((ctmp = (char (*)[72])malloc(n*sizeof(char[72]))) == 0x0) {
d1491 1
a1491 2
      int k = mapto[i];
      memcpy(ctmp[k], cval[i], 72);
d1508 1
a1508 2
      int k = mapto[i];
      itmp[k] = ival[i];
@


8.6.1.6
log
@Replaced all instances of strncat() with strncpy().
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6.1.5 2026/04/22 13:13:01 mcalabre Exp mcalabre $
d281 1
a281 1
    strncpy(*cp, ".\n", cpe - *cp);
d713 1
a713 1
          strncpy(cp, ".\n", cpe - cp);
d726 1
a726 1
          strncpy(cp, ".\n", cpe - cp);
d780 1
a780 1
          strncpy(cp, ".\n", cpe - cp);
d793 1
a793 1
          strncpy(cp, ".\n", cpe - cp);
@


8.6.1.7
log
@In datfix(), ensure that orig_date is null-terminated.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.6.1.6 2026/04/22 15:15:05 mcalabre Exp mcalabre $
a344 1
    orig_date[71] = '\0';
@


8.5
log
@WCSLIB 8.5
@
text
@d2 2
a3 2
  WCSLIB 8.5 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2025, Mark Calabretta
d21 2
a22 2
  http://www.atnf.csiro.au/people/Mark.Calabretta
  $Id: wcsfix.c,v 8.4.1.2 2025/12/06 13:37:16 mcalabre Exp mcalabre $
@


8.5.1.1
log
@WCSLIB 8.5 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.5 2025/12/06 13:47:41 mcalabre Exp $
@


8.5.1.2
log
@Updated date in copyright notice.
@
text
@d3 1
a3 1
  Copyright (C) 1995-2026, Mark Calabretta
d21 2
a22 2
  http://www.atnf.csiro.au/computing/software/wcs
  $Id: wcsfix.c,v 8.5.1.1 2025/12/06 14:05:15 mcalabre Exp mcalabre $
@


8.4
log
@WCSLIB 8.4
@
text
@d2 2
a3 2
  WCSLIB 8.4 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2024, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 8.3.1.1 2024/05/13 16:35:39 mcalabre Exp mcalabre $
@


8.4.1.1
log
@WCSLIB 8.4 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.4 2024/10/28 13:56:16 mcalabre Exp $
@


8.4.1.2
log
@Updated date in copyright notice.
@
text
@d3 1
a3 1
  Copyright (C) 1995-2025, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 8.4.1.1 2024/10/28 13:58:41 mcalabre Exp mcalabre $
@


8.3
log
@WCSLIB 8.3
@
text
@d2 1
a2 1
  WCSLIB 8.3 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 8.2.1.4 2024/05/13 05:14:44 mcalabre Exp mcalabre $
@


8.3.1.1
log
@WCSLIB 8.3 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.3 2024/05/13 16:33:00 mcalabre Exp $
@


8.2
log
@WCSLIB 8.2
@
text
@d2 2
a3 2
  WCSLIB 8.2 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2023, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 8.1.1.1 2023/07/05 17:15:00 mcalabre Exp mcalabre $
a41 2
extern const int WCSSET;

d85 2
d319 3
a321 3
    const char *dateid;
    char *date;
    double *wcsmjd;
d925 1
a925 1
  if (wcs->flag != WCSSET) {
d1046 1
a1046 1
  if (wcs->flag != WCSSET) {
d1140 1
d1163 1
a1163 1
  if (wcs->flag != WCSSET) {
d1409 1
@


8.2.1.1
log
@WCSLIB 8.2 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.2 2023/11/16 10:00:29 mcalabre Exp $
@


8.2.1.2
log
@Defined WCSSET locally as a static variable matching that in wcs.c.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.2.1.1 2023/11/16 10:05:57 mcalabre Exp mcalabre $
d42 2
a85 2

static const int WCSSET = 137;		// Matching wcs.c
@


8.2.1.3
log
@Updated date in copyright notice.
@
text
@d3 1
a3 1
  Copyright (C) 1995-2024, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 8.2.1.2 2023/11/29 07:40:24 mcalabre Exp mcalabre $
d319 3
a321 3
    const char *dateid = 0x0;
    char *date = 0x0;
    double *wcsmjd = 0x0;
@


8.2.1.4
log
@In celfix(), cylfix(), and wcspcx(), support bypass mode in wcsset().
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.2.1.3 2024/04/02 14:01:58 mcalabre Exp mcalabre $
d925 1
a925 1
  if (abs(wcs->flag) != WCSSET) {
d1046 1
a1046 1
  if (abs(wcs->flag) != WCSSET) {
a1139 1
  wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0;
d1162 1
a1162 1
  if (abs(wcs->flag) != WCSSET) {
a1407 1
  wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0;
@


8.1
log
@WCSLIB 8.1
@
text
@d2 1
a2 1
  WCSLIB 8.1 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 8.0.1.1 2023/07/01 06:22:44 mcalabre Exp mcalabre $
@


8.1.1.1
log
@WCSLIB 8.1 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.1 2023/07/05 17:12:07 mcalabre Exp $
@


8.0
log
@WCSLIB 8.0
@
text
@d2 1
a2 1
  WCSLIB 8.0 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.13.1.2 2023/06/15 04:17:19 mcalabre Exp mcalabre $
@


8.0.1.1
log
@WCSLIB 8.0 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 8.0 2023/07/01 06:15:04 mcalabre Exp $
@


7.13
log
@WCSLIB 7.13
@
text
@d2 2
a3 2
  WCSLIB 7.13 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2022, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 7.12.1.1 2022/09/09 05:00:36 mcalabre Exp mcalabre $
@


7.13.1.1
log
@WCSLIB 7.13 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.13 2022/10/07 03:43:35 mcalabre Exp $
@


7.13.1.2
log
@Updated date in copyright notice.
@
text
@d3 1
a3 1
  Copyright (C) 1995-2023, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 7.13.1.1 2022/10/07 03:45:47 mcalabre Exp mcalabre $
@


7.12
log
@WCSLIB 7.12
@
text
@d2 1
a2 1
  WCSLIB 7.12 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.11.1.1 2022/04/26 06:15:32 mcalabre Exp mcalabre $
@


7.12.1.1
log
@WCSLIB 7.12 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.12 2022/09/09 04:57:58 mcalabre Exp $
@


7.11
log
@WCSLIB 7.11
@
text
@d2 1
a2 1
  WCSLIB 7.11 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.10.1.1 2022/04/23 14:26:54 mcalabre Exp mcalabre $
@


7.11.1.1
log
@WCSLIB 7.11 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.11 2022/04/26 06:13:52 mcalabre Exp $
@


7.10
log
@WCSLIB 7.10
@
text
@d2 1
a2 1
  WCSLIB 7.10 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.9.1.1 2022/03/25 15:16:40 mcalabre Exp mcalabre $
@


7.10.1.1
log
@WCSLIB 7.10 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.10 2022/04/23 14:25:15 mcalabre Exp $
@


7.9
log
@WCSLIB 7.9
@
text
@d2 1
a2 1
  WCSLIB 7.9 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.8.1.1 2022/03/25 06:14:14 mcalabre Exp mcalabre $
@


7.9.1.1
log
@WCSLIB 7.9 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.9 2022/03/25 15:14:48 mcalabre Exp $
@


7.8
log
@WCSLIB 7.8
@
text
@d2 1
a2 1
  WCSLIB 7.8 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.7.1.2 2022/03/25 06:05:59 mcalabre Exp mcalabre $
@


7.8.1.1
log
@WCSLIB 7.8 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.8 2022/03/25 06:11:49 mcalabre Exp $
@


7.7
log
@WCSLIB 7.7
@
text
@d2 2
a3 2
  WCSLIB 7.7 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2021, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 7.6.1.3 2021/05/13 03:53:56 mcalabre Exp mcalabre $
@


7.7.1.1
log
@WCSLIB 7.7 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.7 2021/07/12 06:36:49 mcalabre Exp $
@


7.7.1.2
log
@Updated date in copyright notice.
@
text
@d3 1
a3 1
  Copyright (C) 1995-2022, Mark Calabretta
d22 1
a22 1
  $Id: wcsfix.c,v 7.7.1.1 2021/07/12 06:38:27 mcalabre Exp mcalabre $
@


7.6
log
@WCSLIB 7.6
@
text
@d2 1
a2 1
  WCSLIB 7.6 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.5.1.5 2021/04/13 09:36:00 mcalabre Exp mcalabre $
d356 1
a356 1
	  if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d361 1
a361 1
	  if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d1146 1
a1146 1
static void unscramble(int n, int mapto[], int step, int type, void *vptr);
d1230 6
a1235 1
  int mapto[naxis];
d1350 1
a1350 1
        unscramble(naxis, mapto, naxis, 1, wcs->pc + i);
d1352 4
a1355 4
      unscramble(naxis, mapto, 1, 1, wcs->cdelt);
      unscramble(naxis, mapto, 1, 1, wcs->crval);
      unscramble(naxis, mapto, 1, 2, wcs->cunit);
      unscramble(naxis, mapto, 1, 2, wcs->ctype);
d1372 1
a1372 1
          unscramble(naxis, mapto, naxis, 1, wcs->cd + i);
d1377 1
a1377 1
        unscramble(naxis, mapto, 1, 1, wcs->crota);
d1380 6
a1385 6
      unscramble(naxis, mapto, 1, 3, wcs->colax);
      unscramble(naxis, mapto, 1, 2, wcs->cname);
      unscramble(naxis, mapto, 1, 1, wcs->crder);
      unscramble(naxis, mapto, 1, 1, wcs->csyer);
      unscramble(naxis, mapto, 1, 1, wcs->czphs);
      unscramble(naxis, mapto, 1, 1, wcs->cperi);
d1400 1
a1400 1
      // Distortions?  No. Prior distortions operate on pixel coordinates and
d1405 1
d1411 4
d1418 1
a1418 1
void unscramble(
d1430 5
a1434 1
    double dtmp[n];
d1443 2
d1447 5
a1451 1
    char ctmp[n][72];
d1460 2
d1464 5
a1468 1
    int itmp[n];
d1476 2
d1479 2
@


7.6.1.1
log
@WCSLIB 7.6 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.6 2021/04/13 12:57:01 mcalabre Exp $
@


7.6.1.2
log
@In wcspcx() and unscramble(), replaced use of variable length arrays (VLAs)
with allocated memory due to portability issues.  Also, support for VLAs was
removed from C standards after C99 due to security issues.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.6.1.1 2021/04/13 13:00:35 mcalabre Exp mcalabre $
d356 1
a356 1
          if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d361 1
a361 1
          if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d1146 1
a1146 1
static int unscramble(int n, int mapto[], int step, int type, void *vptr);
d1230 1
a1230 6
  int *mapto = 0x0;
  if ((mapto = (int*)malloc(naxis*sizeof(int))) == 0x0) {
    free(mem);
    return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
  }

d1345 1
a1345 1
        if (unscramble(naxis, mapto, naxis, 1, wcs->pc + i)) goto cleanup;
d1347 4
a1350 4
      if (unscramble(naxis, mapto, 1, 1, wcs->cdelt)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->crval)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 2, wcs->cunit)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 2, wcs->ctype)) goto cleanup;
d1367 1
a1367 1
          if (unscramble(naxis, mapto, naxis, 1, wcs->cd + i)) goto cleanup;
d1372 1
a1372 1
        if (unscramble(naxis, mapto, 1, 1, wcs->crota)) goto cleanup;
d1375 6
a1380 6
      if (unscramble(naxis, mapto, 1, 3, wcs->colax)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 2, wcs->cname)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->crder)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->csyer)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->czphs)) goto cleanup;
      if (unscramble(naxis, mapto, 1, 1, wcs->cperi)) goto cleanup;
d1395 1
a1395 1
      // Distortions?  No.  Prior distortions operate on pixel coordinates and
a1399 1
  free(mapto);
a1404 4

cleanup:
  if (mapto) free(mapto);
  return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
d1408 1
a1408 1
int unscramble(
d1420 1
a1420 5
    double *dtmp;
    if ((dtmp = (double *)malloc(n*sizeof(double))) == 0x0) {
      return 1;
    }

a1428 2
    free(dtmp);

d1431 1
a1431 5
    char *ctmp;
    if ((ctmp = (char *)malloc(n*72*sizeof(char))) == 0x0) {
      return 1;
    }

d1433 1
a1433 1
      memcpy(ctmp+72*mapto[i], cval[i], 72);
d1437 1
a1437 1
      memcpy(cval[i], ctmp+72*i, 72);
a1439 2
    free(ctmp);

d1442 1
a1442 5
    int *itmp;
    if ((itmp = (int *)malloc(n*sizeof(int))) == 0x0) {
      return 1;
    }

a1449 2

    free(itmp);
a1450 2

  return 0;
@


7.6.1.3
log
@In unscramble(), simplified the previous change.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.6.1.2 2021/05/13 03:30:55 mcalabre Exp mcalabre $
d1447 2
a1448 2
    char (*ctmp)[72];
    if ((ctmp = (char (*)[72])malloc(n*72*sizeof(char))) == 0x0) {
d1453 1
a1453 1
      memcpy(ctmp[mapto[i]], cval[i], 72);
d1457 1
a1457 1
      memcpy(cval[i], ctmp[i], 72);
@


7.5
log
@WCSLIB 7.5
@
text
@d2 1
a2 1
  WCSLIB 7.5 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.4.1.1 2021/01/31 02:26:24 mcalabre Exp mcalabre $
d30 4
d35 1
d38 1
d40 1
a40 5
#include "lin.h"
#include "sph.h"
#include "wcs.h"
#include "wcsunits.h"
#include "wcsfix.h"
d138 1
a138 2
  int ifix, status = 0;
  struct wcserr err;
d150 1
d153 1
a153 1
  for (ifix = CDFIX; ifix < NWCSFIX; ifix++) {
a220 3
  int  i, k, naxis, status = FIXERR_NO_CHANGE;
  double *cd;

d228 3
a230 3
  naxis = wcs->naxis;
  status = FIXERR_NO_CHANGE;
  for (i = 0; i < naxis; i++) {
d232 2
a233 2
    cd = wcs->cd + i * naxis;
    for (k = 0; k < naxis; k++, cd++) {
d239 1
a239 1
    for (k = 0; k < naxis; k++, cd += naxis) {
a258 1

a271 1

a280 1

d304 2
a305 5
  const char *dateid;
  char *cp, *date, infomsg[512], orig_date[72];
  int  day, dd, hour = 0, i, jd, minute = 0, month, msec, n4, status, year;
  double bepoch, jepoch, mjd[2], mjdsum, mjdtmp, sec = 0.0, t, *wcsmjd;
  struct wcserr **err;
d308 1
a308 1
  err = &(wcs->err);
d310 2
a311 1
  cp = infomsg;
a312 1
  status = FIXERR_NO_CHANGE;
d314 3
a316 1
  for (i = 0; i < 5; i++) {
d319 3
d345 1
d356 1
d361 1
a362 2

	if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d370 1
d387 1
a387 1
        jd = 2400001 + (int)mjd[0];
d389 2
a390 2
        n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
        dd = 10*(((n4-237)%1461)/4) + 5;
d410 1
a410 1
          msec = dd - 60000 * minute;
d519 1
d525 1
a525 1
      mjdsum = mjd[0] + mjd[1];
d541 1
d559 1
a559 1
          jepoch = 2000.0 + (*wcsmjd - mjd2000) / djy;
d569 1
a569 1
          bepoch = 1900.0 + (*wcsmjd - mjd1900) / dty;
d607 2
a608 1
  const double a = 6378140.0, f = 1.0 / 298.2577;
a610 7
  char   *cp, infomsg[256];
  int    havelbh = 7, havexyz = 7, i, status;
  size_t k;
  double coslat, coslng, d, hgt, lat, lng, n, r2, rho, sinlat, sinlng, x, y,
         z, zeta;
  struct wcserr **err;

d612 1
a612 1
  err = &(wcs->err);
d615 1
d619 1
d646 1
a647 1
  status = FIXERR_NO_CHANGE;
d649 4
d655 1
d658 2
a659 2
    n = a / sqrt(1.0 - e2*sinlat*sinlat);
    rho = n + wcs->obsgeo[5];
d668 1
a668 1
      cp = infomsg;
d712 1
a712 1
    r2 = x*x + y*y;
d715 3
a717 2
    zeta = z;
    for (i = 0; i < 4; i++) {
d725 3
a727 3
    lng = atan2d(y, x);
    lat = asind(sinlat);
    hgt = rho - n;
d732 1
a732 1
      cp = infomsg;
d787 1
a787 1
  r2 = 0.0;
d812 2
a813 4
  char   orig_unit[72], msg[512], msgtmp[192];
  int    i, result, status = FIXERR_NO_CHANGE;
  size_t msglen;
  struct wcserr **err;
d815 1
a815 2
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  err = &(wcs->err);
d817 1
d820 2
a821 1
  for (i = 0; i < wcs->naxis; i++) {
d823 1
a823 1
    result = wcsutrne(ctrl, wcs->cunit[i], &(wcs->err));
d825 1
a825 1
      msglen = strlen(msg);
d828 1
d838 1
a838 1
    msglen = strlen(msg) - 2;
a854 4
  char ctype[9], specsys[9];
  int  i, status;
  struct wcserr **err;

d856 1
a856 1
  err = &(wcs->err);
d858 1
a858 1
  for (i = 0; i < wcs->naxis; i++) {
d860 2
a861 1
    status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys);
a919 5
  int k, status;
  struct celprm *wcscel = &(wcs->cel);
  struct prjprm *wcsprj = &(wcscel->prj);
  struct wcserr **err;

d921 1
a921 1
  err = &(wcs->err);
d924 1
d947 1
a947 1
          for (k = 0; k < wcs->npv; k++) {
d959 2
d971 1
a971 1
      return 0;
d997 1
a997 1
            for (k = 0; k < wcs->npv; k++) {
d1026 1
a1026 1
      return 0;
a1039 6
  unsigned short icnr, indx[NMAX], ncnr;
  int    j, k, stat[4], status;
  double img[4][NMAX], lat, lng, phi[4], phi0, phimax, phimin, pix[4][NMAX],
         *pixj, theta[4], theta0, world[4][NMAX], x, y;
  struct wcserr **err;

d1042 1
a1042 1
  err = &(wcs->err);
d1045 1
d1056 1
a1056 1
  ncnr = 1 << wcs->naxis;
d1058 2
a1059 1
  for (k = 0; k < NMAX; k++) {
d1063 6
a1068 3
  phimin =  1.0e99;
  phimax = -1.0e99;
  for (icnr = 0; icnr < ncnr;) {
d1070 2
a1071 2
    for (j = 0; j < 4; j++, icnr++) {
      pixj = pix[j];
d1073 1
a1073 1
      for (k = 0; k < wcs->naxis; k++) {
d1084 1
a1084 1
      for (j = 0; j < 4; j++) {
d1098 2
a1099 2
  phi0 = (phimin + phimax) / 2.0;
  theta0 = 0.0;
d1101 1
d1112 1
a1112 1
  for (k = 0; k < wcs->naxis; k++) {
d1130 2
a1131 2
  lng =  0.0;
  lat = 90.0;
d1141 310
@


7.5.1.1
log
@WCSLIB 7.5 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.5 2021/03/20 05:54:58 mcalabre Exp $
@


7.5.1.2
log
@In datfix(), consequent on the previous change, don't return status 0 if
no change was made.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.5.1.1 2021/03/20 05:56:42 mcalabre Exp mcalabre $
a356 1
	  if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
a360 1
	  if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d362 2
@


7.5.1.3
log
@Added new function wcspcx().
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.5.1.2 2021/04/09 02:49:00 mcalabre Exp mcalabre $
d30 4
a35 1
#include "tab.h"
d37 1
a37 1
#include "wcserr.h"
a38 5
#include "wcsmath.h"
#include "wcstrig.h"
#include "wcsunits.h"
#include "wcsutil.h"
#include "wtbarr.h"
d972 1
a972 1
      return FIXERR_SUCCESS;
d1027 1
a1027 1
      return FIXERR_SUCCESS;
a1141 312
}

//----------------------------------------------------------------------------

// Helper function used only by wcspcx().
static int unscramble(int n, int mapto[], int step, int type, void *vptr);

int wcspcx(
  struct wcsprm *wcs,
  int dopc,
  int permute,
  double rotn[2])

{
  static const char *function = "wcspcx";

  // Initialize if required.
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  struct wcserr **err = &(wcs->err);

  int status;
  if (wcs->flag != WCSSET) {
    if ((status = wcsset(wcs))) return status;
  }

  // Check for CDi_j usage.
  double *wcscd = wcs->cd;
  if ((wcs->altlin & 1) || !(wcs->altlin & 2)) {
    if ((wcs->altlin & 1) && dopc == 1) {
      // Recompose PCi_j + CDELTi.
      wcscd = wcs->pc;
    } else {
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "CDi_j is not used in this coordinate representation");
    }
  }

  // Check for sequent distortions.
  if (wcs->lin.disseq) {
    return wcserr_set(WCSERR_SET(FIXERR_BAD_COORD_TRANS),
      "Cannot handle coordinate descriptions containing sequent distortions");
  }


  // Allocate memory in bulk for two nxn matrices.
  int naxis = wcs->naxis;
  double *mem;
  if ((mem = calloc(2*naxis*naxis, sizeof(double))) == 0x0) {
    return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
  }

  double *mat = mem;
  double *inv = mem + naxis*naxis;

  // Construct the transpose of CDi_j with each element squared.
  double *matij = mat;
  for (int i = 0; i < naxis; i++) {
    double *cdji = wcscd + i;
    for (int j = 0; j < naxis; j++) {
      *(matij++) = (*cdji) * (*cdji);
      cdji += naxis;
    }
  }

  // Invert the matrix.
  if ((status = matinv(naxis, mat, inv))) {
    return wcserr_set(WCSERR_SET(FIXERR_SINGULAR_MTX),
      "No solution for CDi_j matrix decomposition");
  }

  // Apply scaling.
  double *invij = inv;
  double *pcij = wcs->pc;
  double *cdij = wcscd;
  for (int i = 0; i < naxis; i++) {
    double scl = 0.0;
    for (int j = 0; j < naxis; j++) {
      scl += *(invij++);
    }

    scl = sqrt(scl);
    wcs->cdelt[i] /= scl;

    for (int j = 0; j < naxis; j++) {
      *(pcij++) = *(cdij++) * scl;
    }
  }

  // mapto[i] records where row i of PCi_j should move to.
  int mapto[naxis];
  for (int i = 0; i < naxis; i++) {
    mapto[i] = -1;
  }

  // Ensure that latitude always follows longitude.
  if (wcs->lng >= 0 && wcs->lat >= 0) {
    double *pci = wcs->pc + naxis*wcs->lng;

    // Take the first non-zero element in the row.
    for (int j = 0; j < naxis; j++) {
      if (fabs(pci[j]) != 0.0) {
        mapto[wcs->lng] = j;
        break;
      }
    }

    if (mapto[wcs->lng] == naxis-1) {
      mapto[wcs->lng]--;
    }

    mapto[wcs->lat] = mapto[wcs->lng] + 1;
  }

  // Fill in the rest of the row permutation map.
  for (int j = 0; j < naxis; j++) {
    // Column j.
    double *pcij = wcs->pc + j;
    double colmax = 0.0;

    // Look down the column to find the absolute maximum element.
    for (int i = 0; i < naxis; i++, pcij += naxis) {
      if (!(mapto[i] < 0)) {
        // This row is already mapped.
        continue;
      }

      if (fabs(*pcij) > colmax) {
        mapto[i] = j;
        colmax = fabs(*pcij);
      }
    }
  }


  // Fix the sign of CDELTi.  Celestial axes are special, otherwise diagonal
  // elements of the correctly permuted matrix should be positive.
  for (int i = 0; i < naxis; i++) {
    int chsgn;
    double *pci = wcs->pc + naxis*i;

    // Celestial axes are special.
    if (i == wcs->lng) {
      // Longitude axis - force CDELTi < 0.0.
      chsgn = (wcs->cdelt[i] > 0.0);
    } else if (i == wcs->lat) {
      // Latitude axis - force CDELTi > 0.0.
      chsgn = (wcs->cdelt[i] < 0.0);
    } else {
      chsgn = (pci[mapto[i]] < 0.0);
    }

    if (chsgn) {
      wcs->cdelt[i] = -wcs->cdelt[i];

      for (int j = 0; j < naxis; j++) {
        // Test needed to prevent negative zeros.
        if (pci[j] != 0.0) {
          pci[j] = -pci[j];
        }
      }
    }
  }

  free(mem);

  // Setting bit 3 in wcsprm::altlin stops wcsset() from reconstructing
  // PCi_j and CDELTi from CDi_j.
  wcs->altlin |= 8;


  // Compute rotation angle of each basis vector of the celestial axes.
  if (rotn) {
    if (wcs->lng < 0 || wcs->lat < 0) {
      // No celestial axes.
      rotn[0] = 0.0;
      rotn[1] = 0.0;

    } else {
      double x, y;
      x =  wcs->pc[naxis*wcs->lng + mapto[wcs->lng]];
      y =  wcs->pc[naxis*wcs->lat + mapto[wcs->lng]];
      rotn[0] = atan2d(y, x);

      y = -wcs->pc[naxis*wcs->lng + mapto[wcs->lat]];
      x =  wcs->pc[naxis*wcs->lat + mapto[wcs->lat]];
      rotn[1] = atan2d(y, x);
    }
  }


  // Permute rows?
  if (permute) {
    // Check whether there's anything to unscramble.
    int scrambled = 0;
    for (int i = 0; i < naxis; i++) {
      if (mapto[i] != i) {
        scrambled = 1;
        break;
      }
    }

    if (scrambled) {
      for (int i = 0; i < naxis; i++) {
        // Do columns of the PCi_ja matrix.
        unscramble(naxis, mapto, naxis, 1, wcs->pc + i);
      }
      unscramble(naxis, mapto, 1, 1, wcs->cdelt);
      unscramble(naxis, mapto, 1, 1, wcs->crval);
      unscramble(naxis, mapto, 1, 2, wcs->cunit);
      unscramble(naxis, mapto, 1, 2, wcs->ctype);

      for (int ipv = 0; ipv < wcs->npv; ipv++) {
        // Noting that PVi_ma axis numbers are 1-relative.
        int i = wcs->pv[ipv].i - 1;
        wcs->pv[ipv].i = mapto[i] + 1;
      }

      for (int ips = 0; ips < wcs->nps; ips++) {
        // Noting that PSi_ma axis numbers are 1-relative.
        int i = wcs->ps[ips].i - 1;
        wcs->ps[ips].i = mapto[i] + 1;
      }

      if (wcs->altlin & 2) {
        for (int i = 0; i < naxis; i++) {
          // Do columns of the CDi_ja matrix.
          unscramble(naxis, mapto, naxis, 1, wcs->cd + i);
        }
      }

      if (wcs->altlin & 4) {
        unscramble(naxis, mapto, 1, 1, wcs->crota);
      }

      unscramble(naxis, mapto, 1, 3, wcs->colax);
      unscramble(naxis, mapto, 1, 2, wcs->cname);
      unscramble(naxis, mapto, 1, 1, wcs->crder);
      unscramble(naxis, mapto, 1, 1, wcs->csyer);
      unscramble(naxis, mapto, 1, 1, wcs->czphs);
      unscramble(naxis, mapto, 1, 1, wcs->cperi);

      // Coordinate lookup tables.
      for (int itab = 0; itab < wcs->ntab; itab++) {
        for (int m = 0; m < wcs->tab[itab].M; m++) {
          int i = wcs->tab[itab].map[m];
          wcs->tab[itab].map[m] = mapto[i];
        }
      }

      for (int iwtb = 0; iwtb < wcs->nwtb; iwtb++) {
        int i = wcs->wtb[iwtb].i;
        wcs->wtb[iwtb].i = mapto[i];
      }

      // Distortions?  No. Prior distortions operate on pixel coordinates and
      // therefore are not permuted, and sequent distortions are not handled.
    }
  }


  // Reset the struct.
  if ((status = wcsset(wcs))) return status;

  return FIXERR_SUCCESS;
}


int unscramble(
  int n,
  int mapto[],
  int step,
  int type,
  void *vptr)

{
  if (step == 0) step = 1;

  if (type == 1) {
    double *dval = (double *)vptr;
    double dtmp[n];
    for (int i = 0; i < n; i++) {
      dtmp[mapto[i]] = dval[i*step];
    }

    for (int i = 0; i < n; i++) {
      dval[i*step] = dtmp[i];
    }

  } else if (type == 2) {
    char (*cval)[72] = (char (*)[72])vptr;
    char ctmp[n][72];
    for (int i = 0; i < n; i++) {
      memcpy(ctmp[mapto[i]], cval[i], 72);
    }

    for (int i = 0; i < n; i++) {
      memcpy(cval[i], ctmp[i], 72);
    }

  } else if (type == 3) {
    int *ival = (int *)vptr;
    int itmp[n];
    for (int i = 0; i < n; i++) {
      itmp[mapto[i]] = ival[i];
    }

    for (int i = 0; i < n; i++) {
      ival[i] = itmp[i];
    }
  }

  return 0;
@


7.5.1.4
log
@Converted to C99-style intermingled variable declarations.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.5.1.3 2021/04/10 14:26:36 mcalabre Exp mcalabre $
d138 2
a139 1
  int status = 0;
a150 1
  struct wcserr err;
d153 1
a153 1
  for (int ifix = CDFIX; ifix < NWCSFIX; ifix++) {
d221 3
d231 3
a233 3
  int naxis  = wcs->naxis;
  int status = FIXERR_NO_CHANGE;
  for (int i = 0; i < naxis; i++) {
d235 2
a236 2
    double *cd = wcs->cd + i*naxis;
    for (int k = 0; k < naxis; k++, cd++) {
d242 1
a242 1
    for (int k = 0; k < naxis; k++, cd += naxis) {
d262 1
d276 1
d286 1
d310 5
a314 2
  int  day, hour = 0, minute = 0, month, year;
  double sec = 0.0;
d317 1
a317 1
  struct wcserr **err = &(wcs->err);
d319 1
a319 2
  char infomsg[512];
  char *cp = infomsg;
d321 1
d323 1
a323 3
  int status = FIXERR_NO_CHANGE;

  for (int i = 0; i < 5; i++) {
a325 3
    const char *dateid;
    char *date;
    double *wcsmjd;
a348 1
    char orig_date[72];
a372 1
        double mjd[2], t;
d389 1
a389 1
        int jd = 2400001 + (int)mjd[0];
d391 2
a392 2
        int n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
        int dd = 10*(((n4-237)%1461)/4) + 5;
d412 1
a412 1
          int msec = dd - 60000 * minute;
a520 1
      double mjd[2];
d526 1
a526 1
      double mjdsum = mjd[0] + mjd[1];
a541 1
        double mjdtmp;
d559 1
a559 1
          double jepoch = 2000.0 + (*wcsmjd - mjd2000) / djy;
d569 1
a569 1
          double bepoch = 1900.0 + (*wcsmjd - mjd1900) / dty;
d607 1
a607 2
  const double a  = 6378140.0;
  const double f  = 1.0 / 298.2577;
d610 7
d618 1
a618 1
  struct wcserr **err = &(wcs->err);
a620 1
  int havexyz = 7;
a623 1
  int havelbh = 7;
a649 1
  char infomsg[256];
d651 1
a652 4
  int status = FIXERR_NO_CHANGE;

  size_t k;
  double x, y, z;
a654 1
    double coslat, coslng, sinlat, sinlng;
d657 2
a658 2
    double n = a / sqrt(1.0 - e2*sinlat*sinlat);
    double rho = n + wcs->obsgeo[5];
d667 1
a667 1
      char *cp = infomsg;
d711 1
a711 1
    double r2 = x*x + y*y;
d714 2
a715 3
    double coslat, coslng, sinlat, sinlng;
    double n, rho, zeta = z;
    for (int i = 0; i < 4; i++) {
d723 3
a725 3
    double lng = atan2d(y, x);
    double lat = asind(sinlat);
    double hgt = rho - n;
d730 1
a730 1
      char *cp = infomsg;
d785 1
a785 1
  double d, r2 = 0.0;
d810 5
d816 1
a816 3
  struct wcserr **err = &(wcs->err);

  int status = FIXERR_NO_CHANGE;
a817 1
  char msg[512];
d820 1
a820 2
  for (int i = 0; i < wcs->naxis; i++) {
    char orig_unit[72];
d822 1
a822 1
    int result = wcsutrne(ctrl, wcs->cunit[i], &(wcs->err));
d824 1
a824 1
      size_t msglen = strlen(msg);
a826 1
        char msgtmp[192];
d836 1
a836 1
    size_t msglen = strlen(msg) - 2;
d853 4
d858 1
a858 1
  struct wcserr **err = &(wcs->err);
d860 1
a860 1
  for (int i = 0; i < wcs->naxis; i++) {
d862 1
a862 2
    char ctype[9], specsys[9];
    int status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys);
d921 5
d927 1
a927 1
  struct wcserr **err = &(wcs->err);
a929 1
  int status;
d952 1
a952 1
          for (int k = 0; k < wcs->npv; k++) {
a963 2
      struct celprm *wcscel = &(wcs->cel);
      struct prjprm *wcsprj = &(wcscel->prj);
d1000 1
a1000 1
            for (int k = 0; k < wcs->npv; k++) {
d1043 6
d1051 1
a1051 1
  struct wcserr **err = &(wcs->err);
a1053 1
  int status;
d1064 1
a1064 1
  unsigned short ncnr = 1 << wcs->naxis;
d1066 1
a1066 2
  unsigned short indx[NMAX];
  for (int k = 0; k < NMAX; k++) {
d1070 3
a1072 6
  int    stat[4];
  double img[4][NMAX], phi[4], pix[4][NMAX], theta[4], world[4][NMAX];

  double phimin =  1.0e99;
  double phimax = -1.0e99;
  for (unsigned short icnr = 0; icnr < ncnr;) {
d1074 2
a1075 2
    for (int j = 0; j < 4; j++, icnr++) {
      double *pixj = pix[j];
d1077 1
a1077 1
      for (int k = 0; k < wcs->naxis; k++) {
d1088 1
a1088 1
      for (int j = 0; j < 4; j++) {
d1102 2
a1103 2
  double phi0 = (phimin + phimax) / 2.0;
  double theta0 = 0.0;
a1104 1
  double x, y;
d1115 1
a1115 1
  for (int k = 0; k < wcs->naxis; k++) {
d1133 2
a1134 2
  double lng =  0.0;
  double lat = 90.0;
d1149 1
a1149 1
static void unscramble(int n, int mapto[], int step, int type, void *vptr);
d1411 1
a1411 1
void unscramble(
d1454 2
@


7.5.1.5
log
@In wcspcx(), use proper return code, fix_wcserr[status].
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.5.1.4 2021/04/10 15:42:19 mcalabre Exp mcalabre $
d1163 1
a1163 1
    if ((status = wcsset(wcs))) return fix_wcserr[status];
d1402 1
a1402 1
  if ((status = wcsset(wcs))) return fix_wcserr[status];
@


7.4
log
@WCSLIB 7.4
@
text
@d2 1
a2 1
  WCSLIB 7.4 - an implementation of the FITS WCS standard.
d22 1
a22 1
  $Id: wcsfix.c,v 7.3.1.5 2021/01/28 12:29:30 mcalabre Exp mcalabre $
@


7.4.1.1
log
@WCSLIB 7.4 development branch.
@
text
@d22 1
a22 1
  $Id: wcsfix.c,v 7.4 2021/01/31 02:24:51 mcalabre Exp $
@


7.3
log
@WCSLIB 7.3
@
text
@d2 2
a3 3

  WCSLIB 7.3 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2020, Mark Calabretta
a19 2
  Direct correspondence concerning WCSLIB to mark@@calabretta.id.au

d22 1
a22 1
  $Id: wcsfix.c,v 7.2.1.2 2020/06/03 03:25:37 mcalabre Exp mcalabre $
d42 1
a42 1
/* Maximum number of coordinate axes that can be handled. */
d45 1
a45 1
/* Map status return value to message. */
d59 1
a59 1
/* Map error returns for lower-level routines. */
d61 7
a67 7
  FIXERR_SUCCESS,		/*  0: LINERR_SUCCESS         */
  FIXERR_NULL_POINTER,		/*  1: LINERR_NULL_POINTER    */
  FIXERR_MEMORY,		/*  2: LINERR_MEMORY          */
  FIXERR_SINGULAR_MTX,		/*  3: LINERR_SINGULAR_MTX    */
  FIXERR_BAD_PARAM,		/*  4: LINERR_DISTORT_INIT    */
  FIXERR_NO_REF_PIX_COORD,	/*  5: LINERR_DISTORT         */
  FIXERR_NO_REF_PIX_VAL		/*  6: LINERR_DEDISTORT       */
d71 12
a82 12
  FIXERR_SUCCESS,		/*  0: WCSERR_SUCCESS         */
  FIXERR_NULL_POINTER,		/*  1: WCSERR_NULL_POINTER    */
  FIXERR_MEMORY,		/*  2: WCSERR_MEMORY          */
  FIXERR_SINGULAR_MTX,		/*  3: WCSERR_SINGULAR_MTX    */
  FIXERR_BAD_CTYPE,		/*  4: WCSERR_BAD_CTYPE       */
  FIXERR_BAD_PARAM,		/*  5: WCSERR_BAD_PARAM       */
  FIXERR_BAD_COORD_TRANS,	/*  6: WCSERR_BAD_COORD_TRANS */
  FIXERR_ILL_COORD_TRANS,	/*  7: WCSERR_ILL_COORD_TRANS */
  FIXERR_BAD_CORNER_PIX,	/*  8: WCSERR_BAD_PIX         */
  FIXERR_NO_REF_PIX_VAL,	/*  9: WCSERR_BAD_WORLD       */
  FIXERR_NO_REF_PIX_VAL 	/* 10: WCSERR_BAD_WORLD_COORD */
				/*     ...others not used     */
d85 1
a85 1
/* Convenience macro for invoking wcserr_set(). */
d88 1
a88 1
/*--------------------------------------------------------------------------*/
d126 1
a126 1
/*--------------------------------------------------------------------------*/
d139 10
a148 10
  /* Handling the status values returned from the sub-fixers is trickier than
  it might seem, especially considering that wcs->err may contain an error
  status on input which should be preserved if no translation errors occur.
  The simplest way seems to be to save a copy of wcs->err and clear it before
  each sub-fixer.  The last real error to occur, excluding informative
  messages, is the one returned.

  To get informative messages from spcfix() it must precede celfix() and
  cylfix().  The latter call wcsset() which also translates AIPS-convention
  spectral axes. */
d152 1
a152 1
    /* Clear (delete) wcs->err. */
d182 1
a182 1
      /* No change => no message. */
d186 1
a186 1
      /* Successful translation, but there may be an informative message. */
d194 1
a194 1
      /* An informative message or error message. */
d198 1
a198 1
        /* It was an error, replace the previous one. */
d204 1
a204 1
  /* Restore the last error to occur. */
d214 1
a214 1
/*--------------------------------------------------------------------------*/
d225 1
a225 1
    /* Either we have PCi_ja or there are no CDi_ja. */
d232 1
a232 1
    /* Row of zeros? */
d238 1
a238 1
    /* Column of zeros? */
d246 1
a246 1
    status = 0;
d254 1
a254 1
/*--------------------------------------------------------------------------*/
d300 1
a300 1
  /* MJD of J2000.0 and B1900.0. */
d304 1
a304 1
  /* Days per Julian year and per tropical year. */
d322 2
a323 2
    /* MJDREF is split into integer and fractional parts, wheres MJDOBS and
       the rest are a single value. */
d325 1
a325 1
      /* Note, DATEREF and MJDREF, not DATE-REF and MJD-REF (sigh). */
d350 1
a350 1
      /* Fill in DATE from MJD if possible. */
d353 1
a353 1
        /* See if we have jepoch or bepoch. */
d362 2
d367 1
a367 1
        /* No date information was provided. */
d370 1
a370 1
        /* Calendar date from MJD, with allowance for MJD < 0. */
d372 1
a372 1
          /* MJDREF is already split into integer and fractional parts. */
d376 1
a376 1
            /* Ensure the fractional part lies between 0 and +1. */
d382 1
a382 1
          /* Split it into integer and fractional parts. */
d397 1
a397 1
        /* Write time part only if non-zero. */
d405 1
a405 1
          /* Round to 1ms. */
d413 1
a413 1
          /* Write fractions of a second only if non-zero. */
d422 1
a422 1
        /* Can't be a valid date. */
d429 1
a429 1
      /* Identify the date format. */
d431 1
a431 1
        /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */
d457 1
a457 1
        /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */
d482 1
a482 1
        /* Looks ok, fix it up. */
d488 1
a488 1
          /* Old format DATE-OBS date: DD/MM/YY, also allowing DD/MM/CCYY. */
d497 1
a497 1
          /* Also recognize DD-MM-YY and DD-MM-CCYY */
d506 1
a506 1
          /* Not a valid date format. */
d514 1
a514 1
        /* Doesn't have a time. */
d518 1
a518 1
      /* Compute MJD. */
d536 2
d539 1
a539 1
        /* Check for consistency. */
d556 1
a556 1
          /* Check consistency of JEPOCH. */
d560 1
a560 1
            /* Informational only, no error. */
d566 1
a566 1
          /* Check consistency of BEPOCH. */
d570 1
a570 1
            /* Informational only, no error. */
d586 1
a586 1
      if (status == FIXERR_NO_CHANGE) status = 0;
d597 1
a597 1
/*--------------------------------------------------------------------------*/
d604 1
a604 1
  /* IAU(1976) ellipsoid (as prescribed by WCS Paper VII). */
d618 1
a618 1
  /* Set masks for checking partially-defined coordinate triplets. */
d627 1
a627 1
    /* Make no changes. */
d652 1
a652 1
    /* Compute (x,y,z) from (lng,lat,hgt). */
d663 2
a664 2
      /* One or more of the Cartesian elements was undefined. */
      status = 0;
d699 1
a699 1
        /* Skip the consistency check. */
d705 1
a705 1
    /* Compute (lng,lat,hgt) from (x,y,z). */
d711 1
a711 1
    /* Iterate over the value of zeta. */
d726 2
a727 2
      /* One or more of the Geodetic elements was undefined. */
      status = 0;
d761 1
a761 1
        /* Skip the consistency check. */
d766 1
a766 1
    /* Compute (x,y,z) from (lng,lat,hgt) for consistency checking. */
d782 1
a782 1
  /* Check consistency. */
d801 1
a801 1
/*--------------------------------------------------------------------------*/
d833 1
a833 1
    /* Chop off the trailing ", ". */
d838 1
a838 1
    status = 0;
d844 1
a844 1
/*--------------------------------------------------------------------------*/
d859 1
a859 1
    /* Translate an AIPS-convention spectral type if present. */
d861 2
a862 2
    if (status == 0) {
      /* An AIPS type was found but it may match what we already have. */
d865 1
a865 1
      /* Was specsys translated? */
d870 1
a870 1
        status = 0;
d873 1
a873 1
      /* Was ctype translated?  Have to null-fill for comparing them. */
d876 3
a878 3
        /* ctype was translated... */
        if (status == 0) {
          /* ...and specsys was also. */
d887 1
a887 1
          status = 0;
d893 2
a894 2
      /* Tidy up. */
      if (status == 0) {
d899 1
a899 1
      /* No need to check for others, wcsset() will fail if so. */
d903 1
a903 1
      /* An AIPS spectral type was found but with invalid velref. */
d912 1
a912 1
/*--------------------------------------------------------------------------*/
d927 1
a927 1
  /* Initialize if required. */
d932 1
a932 1
  /* Was an NCP or GLS projection code translated? */
d934 1
a934 1
    /* Check ctype. */
d940 1
a940 1
        /* Allocate space for two more PVi_ma keyvalues. */
d979 8
a986 8
        /* In the AIPS convention, setting the reference longitude and
         * latitude for GLS does not create an oblique graticule.  A non-zero
         * reference longitude introduces an offset in longitude in the normal
         * way, whereas a non-zero reference latitude simply translates the
         * reference point (i.e. the map as a whole) to that latitude.  This
         * might be effected by adjusting CRPIXja but that is complicated by
         * the linear transformation and instead is accomplished here by
         * setting theta_0. */
d988 1
a988 1
          /* Allocate space for three more PVi_ma keyvalues. */
d1015 1
a1015 1
        /* Note that the reference longitude is still zero. */
d1034 1
a1034 1
/*--------------------------------------------------------------------------*/
d1051 1
a1051 1
  /* Initialize if required. */
d1056 1
a1056 1
  /* Check that we have a cylindrical projection. */
d1061 1
a1061 1
  /* Compute the native longitude in each corner of the image. */
d1071 1
a1071 1
    /* Do four corners at a time. */
d1095 1
a1095 1
  /* Any changes needed? */
d1099 1
a1099 1
  /* Compute the new reference pixel coordinates. */
d1124 1
a1124 1
  /* Compute celestial coordinates at the new reference pixel. */
d1130 1
a1130 1
  /* Compute native coordinates of the celestial pole. */
@


7.3.1.1
log
@WCSLIB 7.3 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 7.3 2020/06/03 03:37:02 mcalabre Exp $
@


7.3.1.2
log
@Switched to C99 style commenting (i.e. using //) within the code.
@
text
@d2 1
d25 1
a25 1
  $Id: wcsfix.c,v 7.3.1.1 2020/06/03 03:38:10 mcalabre Exp mcalabre $
d45 1
a45 1
// Maximum number of coordinate axes that can be handled.
d48 1
a48 1
// Map status return value to message.
d62 1
a62 1
// Map error returns for lower-level routines.
d64 7
a70 7
  FIXERR_SUCCESS,		//  0: LINERR_SUCCESS
  FIXERR_NULL_POINTER,		//  1: LINERR_NULL_POINTER
  FIXERR_MEMORY,		//  2: LINERR_MEMORY
  FIXERR_SINGULAR_MTX,		//  3: LINERR_SINGULAR_MTX
  FIXERR_BAD_PARAM,		//  4: LINERR_DISTORT_INIT
  FIXERR_NO_REF_PIX_COORD,	//  5: LINERR_DISTORT
  FIXERR_NO_REF_PIX_VAL		//  6: LINERR_DEDISTORT
d74 12
a85 12
  FIXERR_SUCCESS,		//  0: WCSERR_SUCCESS
  FIXERR_NULL_POINTER,		//  1: WCSERR_NULL_POINTER
  FIXERR_MEMORY,		//  2: WCSERR_MEMORY
  FIXERR_SINGULAR_MTX,		//  3: WCSERR_SINGULAR_MTX
  FIXERR_BAD_CTYPE,		//  4: WCSERR_BAD_CTYPE
  FIXERR_BAD_PARAM,		//  5: WCSERR_BAD_PARAM
  FIXERR_BAD_COORD_TRANS,	//  6: WCSERR_BAD_COORD_TRANS
  FIXERR_ILL_COORD_TRANS,	//  7: WCSERR_ILL_COORD_TRANS
  FIXERR_BAD_CORNER_PIX,	//  8: WCSERR_BAD_PIX
  FIXERR_NO_REF_PIX_VAL,	//  9: WCSERR_BAD_WORLD
  FIXERR_NO_REF_PIX_VAL 	// 10: WCSERR_BAD_WORLD_COORD
				//     ...others not used
d88 1
a88 1
// Convenience macro for invoking wcserr_set().
d91 1
a91 1
//----------------------------------------------------------------------------
d129 1
a129 1
//----------------------------------------------------------------------------
d142 10
a151 10
  // Handling the status values returned from the sub-fixers is trickier than
  // it might seem, especially considering that wcs->err may contain an error
  // status on input which should be preserved if no translation errors occur.
  // The simplest way seems to be to save a copy of wcs->err and clear it
  // before each sub-fixer.  The last real error to occur, excluding
  // informative messages, is the one returned.

  // To get informative messages from spcfix() it must precede celfix() and
  // cylfix().  The latter call wcsset() which also translates AIPS-convention
  // spectral axes.
d155 1
a155 1
    // Clear (delete) wcs->err.
d185 1
a185 1
      // No change => no message.
d189 1
a189 1
      // Successful translation, but there may be an informative message.
d197 1
a197 1
      // An informative message or error message.
d201 1
a201 1
        // It was an error, replace the previous one.
d207 1
a207 1
  // Restore the last error to occur.
d217 1
a217 1
//----------------------------------------------------------------------------
d228 1
a228 1
    // Either we have PCi_ja or there are no CDi_ja.
d235 1
a235 1
    // Row of zeros?
d241 1
a241 1
    // Column of zeros?
d257 1
a257 1
//----------------------------------------------------------------------------
d303 1
a303 1
  // MJD of J2000.0 and B1900.0.
d307 1
a307 1
  // Days per Julian year and per tropical year.
d325 2
a326 2
    // MJDREF is split into integer and fractional parts, wheres MJDOBS and
    // the rest are a single value.
d328 1
a328 1
      // Note, DATEREF and MJDREF, not DATE-REF and MJD-REF (sigh).
d353 1
a353 1
      // Fill in DATE from MJD if possible.
d356 1
a356 1
        // See if we have jepoch or bepoch.
d368 1
a368 1
        // No date information was provided.
d371 1
a371 1
        // Calendar date from MJD, with allowance for MJD < 0.
d373 1
a373 1
          // MJDREF is already split into integer and fractional parts.
d377 1
a377 1
            // Ensure the fractional part lies between 0 and +1.
d383 1
a383 1
          // Split it into integer and fractional parts.
d398 1
a398 1
        // Write time part only if non-zero.
d406 1
a406 1
          // Round to 1ms.
d414 1
a414 1
          // Write fractions of a second only if non-zero.
d423 1
a423 1
        // Can't be a valid date.
d430 1
a430 1
      // Identify the date format.
d432 1
a432 1
        // Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]]
d458 1
a458 1
        // Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]]
d483 1
a483 1
        // Looks ok, fix it up.
d489 1
a489 1
          // Old format DATE-OBS date: DD/MM/YY, also allowing DD/MM/CCYY.
d498 1
a498 1
          // Also recognize DD-MM-YY and DD-MM-CCYY
d507 1
a507 1
          // Not a valid date format.
d515 1
a515 1
        // Doesn't have a time.
d519 1
a519 1
      // Compute MJD.
d538 1
a538 1
        // Check for consistency.
d555 1
a555 1
          // Check consistency of JEPOCH.
d559 1
a559 1
            // Informational only, no error.
d565 1
a565 1
          // Check consistency of BEPOCH.
d569 1
a569 1
            // Informational only, no error.
d596 1
a596 1
//----------------------------------------------------------------------------
d603 1
a603 1
  // IAU(1976) ellipsoid (as prescribed by WCS Paper VII).
d617 1
a617 1
  // Set masks for checking partially-defined coordinate triplets.
d626 1
a626 1
    // Make no changes.
d651 1
a651 1
    // Compute (x,y,z) from (lng,lat,hgt).
d662 1
a662 1
      // One or more of the Cartesian elements was undefined.
d698 1
a698 1
        // Skip the consistency check.
d704 1
a704 1
    // Compute (lng,lat,hgt) from (x,y,z).
d710 1
a710 1
    // Iterate over the value of zeta.
d725 1
a725 1
      // One or more of the Geodetic elements was undefined.
d760 1
a760 1
        // Skip the consistency check.
d765 1
a765 1
    // Compute (x,y,z) from (lng,lat,hgt) for consistency checking.
d781 1
a781 1
  // Check consistency.
d800 1
a800 1
//----------------------------------------------------------------------------
d832 1
a832 1
    // Chop off the trailing ", ".
d843 1
a843 1
//----------------------------------------------------------------------------
d858 1
a858 1
    // Translate an AIPS-convention spectral type if present.
d861 1
a861 1
      // An AIPS type was found but it may match what we already have.
d864 1
a864 1
      // Was specsys translated?
d872 1
a872 1
      // Was ctype translated?  Have to null-fill for comparing them.
d875 1
a875 1
        // ctype was translated...
d877 1
a877 1
          // ...and specsys was also.
d892 1
a892 1
      // Tidy up.
d898 1
a898 1
      // No need to check for others, wcsset() will fail if so.
d902 1
a902 1
      // An AIPS spectral type was found but with invalid velref.
d911 1
a911 1
//----------------------------------------------------------------------------
d926 1
a926 1
  // Initialize if required.
d931 1
a931 1
  // Was an NCP or GLS projection code translated?
d933 1
a933 1
    // Check ctype.
d939 1
a939 1
        // Allocate space for two more PVi_ma keyvalues.
d978 8
a985 8
        // In the AIPS convention, setting the reference longitude and
        // latitude for GLS does not create an oblique graticule.  A non-zero
        // reference longitude introduces an offset in longitude in the normal
        // way, whereas a non-zero reference latitude simply translates the
        // reference point (i.e. the map as a whole) to that latitude.  This
        // might be effected by adjusting CRPIXja but that is complicated by
        // the linear transformation and instead is accomplished here by
        // setting theta_0.
d987 1
a987 1
          // Allocate space for three more PVi_ma keyvalues.
d1014 1
a1014 1
        // Note that the reference longitude is still zero.
d1033 1
a1033 1
//----------------------------------------------------------------------------
d1050 1
a1050 1
  // Initialize if required.
d1055 1
a1055 1
  // Check that we have a cylindrical projection.
d1060 1
a1060 1
  // Compute the native longitude in each corner of the image.
d1070 1
a1070 1
    // Do four corners at a time.
d1094 1
a1094 1
  // Any changes needed?
d1098 1
a1098 1
  // Compute the new reference pixel coordinates.
d1123 1
a1123 1
  // Compute celestial coordinates at the new reference pixel.
d1129 1
a1129 1
  // Compute native coordinates of the celestial pole.
@


7.3.1.3
log
@In datfix(), return status 0 if an informational message is set in
wcsprm::err.
@
text
@d24 1
a24 1
  $Id: wcsfix.c,v 7.3.1.2 2020/08/17 12:10:44 mcalabre Exp mcalabre $
d248 1
a248 1
    status = FIXERR_SUCCESS;
a363 2

	if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
a535 2
        if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;

d584 1
a584 1
      if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS;
d662 1
a662 1
      status = FIXERR_SUCCESS;
d725 1
a725 1
      status = FIXERR_SUCCESS;
d836 1
a836 1
    status = FIXERR_SUCCESS;
d859 1
a859 1
    if (status == FIXERR_SUCCESS) {
d868 1
a868 1
        status = FIXERR_SUCCESS;
d875 1
a875 1
        if (status == FIXERR_SUCCESS) {
d885 1
a885 1
          status = FIXERR_SUCCESS;
d892 1
a892 1
      if (status == FIXERR_SUCCESS) {
@


7.3.1.4
log
@Updated date in copyright notice.
@
text
@d3 1
a3 1
  Copyright (C) 1995-2021, Mark Calabretta
d24 1
a24 1
  $Id: wcsfix.c,v 7.3.1.3 2020/08/17 12:51:45 mcalabre Exp mcalabre $
@


7.3.1.5
log
@Removed my email address from the copyright notice.
@
text
@d20 2
d24 1
a24 1
  $Id: wcsfix.c,v 7.3.1.4 2021/01/19 14:09:10 mcalabre Exp mcalabre $
@


7.2
log
@WCSLIB 7.2
@
text
@d3 1
a3 1
  WCSLIB 7.2 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 7.1.1.1 2019/12/31 13:28:19 mcalabre Exp mcalabre $
d328 1
d333 1
a333 1
      dateid = "OBS";
d337 1
a337 1
      dateid = "BEG";
d341 1
a341 1
      dateid = "AVG";
d345 1
a345 1
      dateid = "END";
d425 1
a425 1
        sprintf(newline(&cp), "Invalid DATE-%s format '%s' is too short",
d435 1
a435 1
          sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid, date);
d442 1
a442 1
            sprintf(newline(&cp), "Invalid time in DATE-%s '%s'", dateid,
d461 1
a461 1
          sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid, date);
d468 1
a468 1
            sprintf(newline(&cp), "Invalid time in DATE-%s '%s'", dateid,
d492 1
a492 1
            sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid,
d501 1
a501 1
            sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid,
d509 1
a509 1
          sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid, date);
d534 1
a534 1
        sprintf(newline(&cp), "Set MJD-%s to %.6f from DATE-%s", dateid,
d548 1
a548 1
            "Invalid parameter values: MJD-%s and DATE-%s are inconsistent",
d578 1
a578 1
        sprintf(newline(&cp), "Set DATE-%s to '%s' from MJD-%s", dateid, date,
d581 1
a581 1
        sprintf(newline(&cp), "Changed DATE-%s from '%s' to '%s'", dateid,
@


7.2.1.1
log
@WCSLIB 7.2 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 7.2 2020/03/09 07:31:23 mcalabre Exp $
@


7.2.1.2
log
@In datfix(), error messages should refer to DATEREF and MJDREF, not DATE-REF
and MJD-REF.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 7.2.1.1 2020/03/09 07:34:50 mcalabre Exp mcalabre $
a327 1
      /* Note, DATEREF and MJDREF, not DATE-REF and MJD-REF (sigh). */
d332 1
a332 1
      dateid = "-OBS";
d336 1
a336 1
      dateid = "-BEG";
d340 1
a340 1
      dateid = "-AVG";
d344 1
a344 1
      dateid = "-END";
d424 1
a424 1
        sprintf(newline(&cp), "Invalid DATE%s format '%s' is too short",
d434 1
a434 1
          sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date);
d441 1
a441 1
            sprintf(newline(&cp), "Invalid time in DATE%s '%s'", dateid,
d460 1
a460 1
          sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date);
d467 1
a467 1
            sprintf(newline(&cp), "Invalid time in DATE%s '%s'", dateid,
d491 1
a491 1
            sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid,
d500 1
a500 1
            sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid,
d508 1
a508 1
          sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date);
d533 1
a533 1
        sprintf(newline(&cp), "Set MJD%s to %.6f from DATE%s", dateid,
d547 1
a547 1
            "Invalid parameter values: MJD%s and DATE%s are inconsistent",
d577 1
a577 1
        sprintf(newline(&cp), "Set DATE%s to '%s' from MJD%s", dateid, date,
d580 1
a580 1
        sprintf(newline(&cp), "Changed DATE%s from '%s' to '%s'", dateid,
@


7.1
log
@WCSLIB 7.1
@
text
@d3 1
a3 1
  WCSLIB 7.1 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 6.4.1.5 2019/12/31 03:28:03 mcalabre Exp mcalabre $
@


7.1.1.1
log
@WCSLIB 7.1 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 7.1 2019/12/31 13:25:19 mcalabre Exp $
@


6.4
log
@WCSLIB 6.4
@
text
@d3 2
a4 2
  WCSLIB 6.4 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2019, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 6.3.1.1 2019/07/12 07:34:37 mcalabre Exp mcalabre $
d286 1
a286 1
  int k;
d314 1
a314 1
  double bepoch, jepoch, mjd, sec = 0.0, t, *wcsmjd;
d324 3
a326 1
  for (i = 0; i < 4; i++) {
d328 4
d335 1
a335 1
    } else if (i == 1) {
d339 1
a339 1
    } else if (i == 2) {
d343 1
a343 1
    } else if (i == 3) {
a349 1
    mjd = *wcsmjd;
d352 1
d354 1
a354 1
      if (i == 0 && undefined(mjd)) {
d357 2
a358 3
          mjd = mjd2000 + (wcs->jepoch - 2000.0)*djy;
          *wcsmjd = mjd;
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from JEPOCH", mjd);
d361 2
a362 3
          mjd = mjd1900 + (wcs->bepoch - 1900.0)*dty;
          *wcsmjd = mjd;
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from BEPOCH", mjd);
d366 1
a366 1
      if (undefined(mjd)) {
d370 18
a387 2
        /* Calendar date from MJD. */
        jd = 2400001 + (int)mjd;
d398 1
a398 1
        if ((t = mjd - (int)mjd) > 0.0) {
d487 1
a487 1
        if (i == 0 && date[2] == '/' && date[5] == '/') {
d496 1
a496 1
        } else if (i == 0 && date[2] == '-' && date[5] == '-') {
d519 6
a524 5
      mjd = (double)((1461*(year - (12-month)/10 + 4712))/4
            + (306*((month+9)%12) + 5)/10
            - (3*((year - (12-month)/10 + 4900)/100))/4
            + day - 2399904)
            + (hour + (minute + sec/60.0)/60.0)/24.0;
d527 8
a534 3
        *wcsmjd = mjd;
        sprintf(newline(&cp), "Set MJD-%s to %.6f from DATE-%s", dateid, mjd,
          dateid);
d538 7
a544 1
        if (fabs(mjd - *wcsmjd) > 0.001) {
d552 1
a552 1
      if (i == 0) {
d555 1
a555 1
          jepoch = 2000.0 + (mjd - mjd2000) / djy;
d557 1
a557 1
          if (fabs(jepoch - wcs->jepoch) > 0.000002) {
d565 1
a565 1
          bepoch = 1900.0 + (mjd - mjd1900) / dty;
d567 1
a567 1
          if (fabs(bepoch - wcs->bepoch) > 0.000002) {
d607 2
a608 1
  int    havelbh = 7, havexyz = 7, i, k, status;
d806 3
a808 2
  int  i, msglen, result, status = FIXERR_NO_CHANGE;
  char orig_unit[72], msg[512], msgtmp[192];
d817 1
a817 1
    strncpy(orig_unit, wcs->cunit[i], 72);
@


6.4.1.1
log
@WCSLIB 6.4 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.4 2019/08/15 09:30:18 mcalabre Exp $
@


6.4.1.2
log
@In unitfix(), quench a nuisance warning from gcc 9.2.0 with
-Wstringop-truncation (via -Wall).
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.4.1.1 2019/08/15 09:31:29 mcalabre Exp mcalabre $
d783 1
a783 1
    strncpy(orig_unit, wcs->cunit[i], 71);
@


6.4.1.3
log
@Extended datfix() to handle MJDREF/DATEREF, overlooked in the changes at
5.20.1.2.  Also fixed the handling of MJD < 0.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.4.1.3 2019/12/06 05:43:47 mcalabre Exp mcalabre $
d314 1
a314 1
  double bepoch, jepoch, mjd[2], mjdsum, mjdtmp, sec = 0.0, t, *wcsmjd;
d324 1
a324 3
  for (i = 0; i < 5; i++) {
    /* MJDREF is split into integer and fractional parts, wheres MJDOBS and
       the rest are a single value. */
a325 4
      dateid = "REF";
      date   = wcs->dateref;
      wcsmjd = wcs->mjdref;
    } else if (i == 1) {
d329 1
a329 1
    } else if (i == 2) {
d333 1
a333 1
    } else if (i == 3) {
d337 1
a337 1
    } else if (i == 4) {
d344 1
a346 1
      /* Fill in DATE from MJD if possible. */
d348 1
a348 1
      if (i == 1 && undefined(*wcsmjd)) {
d351 3
a353 2
          *wcsmjd = mjd2000 + (wcs->jepoch - 2000.0)*djy;
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from JEPOCH", *wcsmjd);
d356 3
a358 2
          *wcsmjd = mjd1900 + (wcs->bepoch - 1900.0)*dty;
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from BEPOCH", *wcsmjd);
d362 1
a362 1
      if (undefined(*wcsmjd)) {
d366 2
a367 18
        /* Calendar date from MJD, with allowance for MJD < 0. */
        if (i == 0) {
          /* MJDREF is already split into integer and fractional parts. */
          mjd[0] = wcsmjd[0];
          mjd[1] = wcsmjd[1];
          if (1.0 < mjd[1]) {
            /* Ensure the fractional part lies between 0 and +1. */
            t = floor(mjd[1]);
            mjd[0] += t;
            mjd[1] -= t;
          }
        } else {
          /* Split it into integer and fractional parts. */
          mjd[0] = floor(*wcsmjd);
          mjd[1] = *wcsmjd - mjd[0];
        }

        jd = 2400001 + (int)mjd[0];
d378 1
a378 1
        if (0.0 < (t = mjd[1])) {
d467 1
a467 1
        if (i == 1 && date[2] == '/' && date[5] == '/') {
d476 1
a476 1
        } else if (i == 1 && date[2] == '-' && date[5] == '-') {
d499 5
a503 6
      mjd[0] = (double)((1461*(year - (12-month)/10 + 4712))/4
               + (306*((month+9)%12) + 5)/10
               - (3*((year - (12-month)/10 + 4900)/100))/4
               + day - 2399904);
      mjd[1] = (hour + (minute + sec/60.0)/60.0)/24.0;
      mjdsum = mjd[0] + mjd[1];
d506 3
a508 8
        if (i == 0) {
          wcsmjd[0] = mjd[0];
          wcsmjd[1] = mjd[1];
        } else {
          *wcsmjd = mjdsum;
        }
        sprintf(newline(&cp), "Set MJD-%s to %.6f from DATE-%s", dateid,
          mjdsum, dateid);
d512 1
a512 7
        if (i == 0) {
          mjdtmp = wcsmjd[0] + wcsmjd[1];
        } else {
          mjdtmp = *wcsmjd;
        }

        if (0.001 < fabs(mjdsum - mjdtmp)) {
d520 1
a520 1
      if (i == 1) {
d523 1
a523 1
          jepoch = 2000.0 + (*wcsmjd - mjd2000) / djy;
d525 1
a525 1
          if (0.000002 < fabs(jepoch - wcs->jepoch)) {
d533 1
a533 1
          bepoch = 1900.0 + (*wcsmjd - mjd1900) / dty;
d535 1
a535 1
          if (0.000002 < fabs(bepoch - wcs->bepoch)) {
@


6.4.1.4
log
@In newline(), obsfix(), and unitfix(), use the size_t data type correctly to
quell warnings from Microsoft Visual C++ (MSVC).
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.4.1.3 2019/12/12 02:32:25 mcalabre Exp mcalabre $
d286 1
a286 1
  size_t k;
d607 1
a607 2
  int    havelbh = 7, havexyz = 7, i, status;
  size_t k;
d805 2
a806 3
  char   orig_unit[72], msg[512], msgtmp[192];
  int    i, result, status = FIXERR_NO_CHANGE;
  size_t msglen;
@


6.4.1.5
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2020, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 6.4.1.4 2019/12/16 04:38:47 mcalabre Exp mcalabre $
@


6.3
log
@WCSLIB 6.3
@
text
@d3 1
a3 1
  WCSLIB 6.3 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 6.2.1.4 2019/07/08 05:14:24 mcalabre Exp mcalabre $
@


6.3.1.1
log
@WCSLIB 6.3 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.3 2019/07/12 07:33:39 mcalabre Exp $
@


6.2
log
@WCSLIB 6.2
@
text
@d3 2
a4 2
  WCSLIB 6.2 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2018, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 6.1.1.1 2018/10/19 08:36:15 mcalabre Exp mcalabre $
d131 6
a136 2
int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[],
            struct wcserr info[])
d276 1
a276 1
  char ctmp[72];
d740 4
d774 1
a774 1
  char orig_unit[72], msg[512], msgtmp[72];
@


6.2.1.1
log
@WCSLIB 6.2 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.2 2018/10/20 10:03:13 mcalabre Exp $
@


6.2.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2019, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 6.2.1.1 2018/10/20 10:04:15 mcalabre Exp mcalabre $
@


6.2.1.3
log
@In write_date() and unitfix(), avert -Wformat-overflow warnings from
gcc 8.3.0 with -DFORTIFY_SOURCE=2.  In obsfix(), avert a -Wuninitilaized
warning.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.2.1.2 2019/05/10 01:56:50 mcalabre Exp mcalabre $
d272 1
a272 1
  char ctmp[32];
a735 4

  } else {
    return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
      "Observatory coordinates incomplete");
d766 1
a766 1
  char orig_unit[72], msg[512], msgtmp[192];
@


6.2.1.4
log
@Whitespace.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.2.1.3 2019/07/08 03:39:51 mcalabre Exp mcalabre $
d131 2
a132 6
int wcsfixi(
  int ctrl,
  const int naxis[],
  struct wcsprm *wcs,
  int stat[],
  struct wcserr info[])
@


6.1
log
@WCSLIB 6.1
@
text
@d3 1
a3 1
  WCSLIB 6.1 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.20.1.2 2018/10/19 07:03:04 mcalabre Exp mcalabre $
@


6.1.1.1
log
@WCSLIB 6.1 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 6.1 2018/10/19 08:35:17 mcalabre Exp $
@


5.20
log
@WCSLIB 5.20
@
text
@d3 1
a3 1
  WCSLIB 5.20 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.19.1.1 2018/07/26 15:41:40 mcalabre Exp mcalabre $
d35 1
d106 4
d161 3
d268 1
d278 16
d299 12
a310 4
  char orig_dateobs[72];
  char *dateobs;
  int  day, dd, hour = 0, jd, minute = 0, month, msec, n4, year;
  double mjdobs, sec = 0.0, t;
d316 22
a337 6
  dateobs = wcs->dateobs;
  strncpy(orig_dateobs, dateobs, 72);
  if (dateobs[0] == '\0') {
    if (undefined(wcs->mjdobs)) {
     /* No date information was provided. */
      return FIXERR_NO_CHANGE;
d339 2
a340 3
    } else {
      /* Calendar date from MJD. */
      jd = 2400001 + (int)wcs->mjdobs;
d342 1
a342 2
      n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
      dd = 10*(((n4-237)%1461)/4) + 5;
d344 11
a354 24
      year  = n4/1461 - 4712;
      month = (2 + dd/306)%12 + 1;
      day   = (dd%306)/10 + 1;
      sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day);

      /* Write time part only if non-zero. */
      if ((t = wcs->mjdobs - (int)wcs->mjdobs) > 0.0) {
        t *= 24.0;
        hour = (int)t;
        t = 60.0 * (t - hour);
        minute = (int)t;
        sec    = 60.0 * (t - minute);

        /* Round to 1ms. */
        dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005));
        hour = dd / 3600000;
        dd -= 3600000 * hour;
        minute = dd / 60000;
        msec = dd - 60000 * minute;
        sprintf(dateobs+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);

        /* Write fractions of a second only if non-zero. */
        if (msec%1000) {
          sprintf(dateobs+19, ".%.3d", msec%1000);
a356 1
    }
d358 9
a366 6
  } else {
    if (strlen(dateobs) < 8) {
      /* Can't be a valid date. */
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Invalid parameter value: date string too short '%s'", dateobs);
    }
d368 25
a392 21
    /* Identify the date format. */
    if (dateobs[4] == '-' && dateobs[7] == '-') {
      /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */
      if (sscanf(dateobs, "%4d-%2d-%2d", &year, &month, &day) < 3) {
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
          "Invalid parameter value: invalid date '%s'", dateobs);
      }

      if (dateobs[10] == 'T') {
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
            "Invalid parameter value: invalid time '%s'", dateobs+11);
        }
      } else if (dateobs[10] == ' ') {
        hour = 0;
        minute = 0;
        sec = 0.0;
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
          write_date(dateobs+10, hour, minute, sec);
        } else {
          dateobs[10] = 'T';
d396 7
a402 5
    } else if (dateobs[4] == '/' && dateobs[7] == '/') {
      /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */
      if (sscanf(dateobs, "%4d/%2d/%2d", &year, &month, &day) < 3) {
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
          "Invalid parameter value: invalid date '%s'", dateobs);
d405 7
a411 4
      if (dateobs[10] == 'T') {
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
            "Invalid parameter value: invalid time '%s'", dateobs+11);
d413 68
a480 6
      } else if (dateobs[10] == ' ') {
        hour = 0;
        minute = 0;
        sec = 0.0;
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
          write_date(dateobs+10, hour, minute, sec);
d482 4
a485 1
          dateobs[10] = 'T';
d487 5
d494 21
a514 3
      /* Looks ok, fix it up. */
      dateobs[4]  = '-';
      dateobs[7]  = '-';
d516 9
a524 6
    } else {
      if (dateobs[2] == '/' && dateobs[5] == '/') {
        /* Old format date: DD/MM/YY, also allowing DD/MM/CCYY. */
        if (sscanf(dateobs, "%2d/%2d/%4d", &day, &month, &year) < 3) {
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
            "Invalid parameter value: invalid date '%s'", dateobs);
d527 8
a534 5
      } else if (dateobs[2] == '-' && dateobs[5] == '-') {
        /* Also recognize DD-MM-YY and DD-MM-CCYY */
        if (sscanf(dateobs, "%2d-%2d-%4d", &day, &month, &year) < 3) {
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
            "Invalid parameter value: invalid date '%s'", dateobs);
d536 2
d539 4
d544 2
a545 3
        /* Not a valid date format. */
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
          "Invalid parameter value: invalid date '%s'", dateobs);
d548 12
a559 1
      if (year < 100) year += 1900;
d561 40
a600 2
      /* Doesn't have a time. */
      sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day);
d602 34
d637 8
a644 6
    /* Compute MJD. */
    mjdobs = (double)((1461*(year - (12-month)/10 + 4712))/4
             + (306*((month+9)%12) + 5)/10
             - (3*((year - (12-month)/10 + 4900)/100))/4
             + day - 2399904)
             + (hour + (minute + sec/60.0)/60.0)/24.0;
d646 78
a723 7
    if (undefined(wcs->mjdobs)) {
      wcs->mjdobs = mjdobs;
    } else {
      /* Check for consistency. */
      if (fabs(mjdobs - wcs->mjdobs) > 0.5) {
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
          "Invalid parameter value: inconsistent date '%s'", dateobs);
d726 10
a737 3
  if (strncmp(orig_dateobs, dateobs, 72)) {
    wcserr_set(WCSERR_SET(FIXERR_DATE_FIX),
      "Changed '%s' to '%s'", orig_dateobs, dateobs);
d739 13
a751 1
    return 0;
d754 1
a754 1
  return FIXERR_NO_CHANGE;
d757 1
a762 2
  int  i, k, result, status = FIXERR_NO_CHANGE;
  char orig_unit[80], msg[WCSERR_MSG_LENGTH], msgtmp[WCSERR_MSG_LENGTH];
d764 3
d772 1
a772 1
  strncpy(msg, "Changed units: ", WCSERR_MSG_LENGTH);
d775 1
a775 1
    strncpy(orig_unit, wcs->cunit[i], 80);
d778 5
a782 5
      k = strlen(msg);
      if (k < WCSERR_MSG_LENGTH-1) {
        wcsutil_null_fill(80, orig_unit);
        sprintf(msgtmp, "'%s' -> '%s', ", orig_unit, wcs->cunit[i]);
        strncpy(msg+k, msgtmp, WCSERR_MSG_LENGTH-1-k);
d790 2
a791 2
    k = strlen(msg) - 2;
    msg[k] = '\0';
@


5.20.1.1
log
@WCSLIB 5.20 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.20 2018/10/05 05:32:41 mcalabre Exp $
@


5.20.1.2
log
@Extended datfix() to handle additional time-related WCS keywords (Paper VII).
Added obsfix() to check the consistency of OBSGEO-[XYZLBH] keywords.  Revamped
the informative message handling in unitfix().
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.20.1.1 2018/10/05 05:33:33 mcalabre Exp mcalabre $
a34 1
#include "wcstrig.h"
a104 4
  if ((stat[OBSFIX] = obsfix(0, wcs)) > 0) {
    status = 1;
  }

a155 3
    case OBSFIX:
      stat[ifix] = obsfix(0, wcs);
      break;
a259 1

a268 16

static char *newline(char **cp)

{
  int k;

  if ((k = strlen(*cp))) {
    *cp += k;
    strcat(*cp, ".\n");
    *cp += 2;
  }

  return *cp;
}


d274 4
a277 12
  /* MJD of J2000.0 and B1900.0. */
  const double mjd2000 = 51544.5;
  const double mjd1900 = 15019.81352;

  /* Days per Julian year and per tropical year. */
  const double djy = 365.25;
  const double dty = 365.242198781;

  const char *dateid;
  char *cp, *date, infomsg[512], orig_date[72];
  int  day, dd, hour = 0, i, jd, minute = 0, month, msec, n4, status, year;
  double bepoch, jepoch, mjd, sec = 0.0, t, *wcsmjd;
d283 6
a288 3
  cp = infomsg;
  *cp = '\0';
  status = FIXERR_NO_CHANGE;
d290 3
a292 18
  for (i = 0; i < 4; i++) {
    if (i == 0) {
      dateid = "OBS";
      date   = wcs->dateobs;
      wcsmjd = &(wcs->mjdobs);
    } else if (i == 1) {
      dateid = "BEG";
      date   = wcs->datebeg;
      wcsmjd = &(wcs->mjdbeg);
    } else if (i == 2) {
      dateid = "AVG";
      date   = wcs->dateavg;
      wcsmjd = &(wcs->mjdavg);
    } else if (i == 3) {
      dateid = "END";
      date   = wcs->dateend;
      wcsmjd = &(wcs->mjdend);
    }
d294 2
a295 2
    strncpy(orig_date, date, 72);
    mjd = *wcsmjd;
d297 24
a320 13
    if (date[0] == '\0') {

      if (i == 0 && undefined(mjd)) {
        /* See if we have jepoch or bepoch. */
        if (!undefined(wcs->jepoch)) {
          mjd = mjd2000 + (wcs->jepoch - 2000.0)*djy;
          *wcsmjd = mjd;
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from JEPOCH", mjd);

        } else if (!undefined(wcs->bepoch)) {
          mjd = mjd1900 + (wcs->bepoch - 1900.0)*dty;
          *wcsmjd = mjd;
          sprintf(newline(&cp), "Set MJD-OBS to %.6f from BEPOCH", mjd);
d323 1
d325 6
a330 2
      if (undefined(mjd)) {
        /* No date information was provided. */
d332 21
a352 32
      } else {
        /* Calendar date from MJD. */
        jd = 2400001 + (int)mjd;

        n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
        dd = 10*(((n4-237)%1461)/4) + 5;

        year  = n4/1461 - 4712;
        month = (2 + dd/306)%12 + 1;
        day   = (dd%306)/10 + 1;
        sprintf(date, "%.4d-%.2d-%.2d", year, month, day);

        /* Write time part only if non-zero. */
        if ((t = mjd - (int)mjd) > 0.0) {
          t *= 24.0;
          hour = (int)t;
          t = 60.0 * (t - hour);
          minute = (int)t;
          sec    = 60.0 * (t - minute);

          /* Round to 1ms. */
          dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005));
          hour = dd / 3600000;
          dd -= 3600000 * hour;
          minute = dd / 60000;
          msec = dd - 60000 * minute;
          sprintf(date+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);

          /* Write fractions of a second only if non-zero. */
          if (msec%1000) {
            sprintf(date+19, ".%.3d", msec%1000);
          }
d356 5
a360 7
    } else {
      if (strlen(date) < 8) {
        /* Can't be a valid date. */
        status = FIXERR_BAD_PARAM;
        sprintf(newline(&cp), "Invalid DATE-%s format '%s' is too short",
          dateid, date);
        continue;
d363 4
a366 7
      /* Identify the date format. */
      if (date[4] == '-' && date[7] == '-') {
        /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */
        if (sscanf(date, "%4d-%2d-%2d", &year, &month, &day) < 3) {
          status = FIXERR_BAD_PARAM;
          sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid, date);
          continue;
d368 6
a373 68

        if (date[10] == 'T') {
          if (parse_date(date+11, &hour, &minute, &sec)) {
            status = FIXERR_BAD_PARAM;
            sprintf(newline(&cp), "Invalid time in DATE-%s '%s'", dateid,
              date+11);
            continue;
          }
        } else if (date[10] == ' ') {
          hour = 0;
          minute = 0;
          sec = 0.0;
          if (parse_date(date+11, &hour, &minute, &sec)) {
            write_date(date+10, hour, minute, sec);
          } else {
            date[10] = 'T';
          }
        }

      } else if (date[4] == '/' && date[7] == '/') {
        /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */
        if (sscanf(date, "%4d/%2d/%2d", &year, &month, &day) < 3) {
          status = FIXERR_BAD_PARAM;
          sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid, date);
          continue;
        }

        if (date[10] == 'T') {
          if (parse_date(date+11, &hour, &minute, &sec)) {
            status = FIXERR_BAD_PARAM;
            sprintf(newline(&cp), "Invalid time in DATE-%s '%s'", dateid,
              date+11);
            continue;
          }
        } else if (date[10] == ' ') {
          hour = 0;
          minute = 0;
          sec = 0.0;
          if (parse_date(date+11, &hour, &minute, &sec)) {
            write_date(date+10, hour, minute, sec);
          } else {
            date[10] = 'T';
          }
        }

        /* Looks ok, fix it up. */
        date[4]  = '-';
        date[7]  = '-';

      } else {
        if (i == 0 && date[2] == '/' && date[5] == '/') {
          /* Old format DATE-OBS date: DD/MM/YY, also allowing DD/MM/CCYY. */
          if (sscanf(date, "%2d/%2d/%4d", &day, &month, &year) < 3) {
            status = FIXERR_BAD_PARAM;
            sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid,
              date);
            continue;
          }

        } else if (i == 0 && date[2] == '-' && date[5] == '-') {
          /* Also recognize DD-MM-YY and DD-MM-CCYY */
          if (sscanf(date, "%2d-%2d-%4d", &day, &month, &year) < 3) {
            status = FIXERR_BAD_PARAM;
            sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid,
              date);
            continue;
          }

d375 1
a375 4
          /* Not a valid date format. */
          status = FIXERR_BAD_PARAM;
          sprintf(newline(&cp), "Invalid DATE-%s format '%s'", dateid, date);
          continue;
a376 5

        if (year < 100) year += 1900;

        /* Doesn't have a time. */
        sprintf(date, "%.4d-%.2d-%.2d", year, month, day);
d379 3
a381 11
      /* Compute MJD. */
      mjd = (double)((1461*(year - (12-month)/10 + 4712))/4
            + (306*((month+9)%12) + 5)/10
            - (3*((year - (12-month)/10 + 4900)/100))/4
            + day - 2399904)
            + (hour + (minute + sec/60.0)/60.0)/24.0;

      if (undefined(*wcsmjd)) {
        *wcsmjd = mjd;
        sprintf(newline(&cp), "Set MJD-%s to %.6f from DATE-%s", dateid, mjd,
          dateid);
d383 6
a388 7
      } else {
        /* Check for consistency. */
        if (fabs(mjd - *wcsmjd) > 0.001) {
          status = FIXERR_BAD_PARAM;
          sprintf(newline(&cp),
            "Invalid parameter values: MJD-%s and DATE-%s are inconsistent",
            dateid, dateid);
a389 1
      }
d391 5
a395 9
      if (i == 0) {
        if (!undefined(wcs->jepoch)) {
          /* Check consistency of JEPOCH. */
          jepoch = 2000.0 + (mjd - mjd2000) / djy;

          if (fabs(jepoch - wcs->jepoch) > 0.000002) {
            /* Informational only, no error. */
            sprintf(newline(&cp), "JEPOCH is inconsistent with DATE-OBS");
          }
a397 16
        if (!undefined(wcs->bepoch)) {
          /* Check consistency of BEPOCH. */
          bepoch = 1900.0 + (mjd - mjd1900) / dty;

          if (fabs(bepoch - wcs->bepoch) > 0.000002) {
            /* Informational only, no error. */
            sprintf(newline(&cp), "BEPOCH is inconsistent with DATE-OBS");
          }
        }
      }
    }

    if (strncmp(orig_date, date, 72)) {
      if (orig_date[0] == '\0') {
        sprintf(newline(&cp), "Set DATE-%s to '%s' from MJD-%s", dateid, date,
          dateid);
d399 3
a401 2
        sprintf(newline(&cp), "Changed DATE-%s from '%s' to '%s'", dateid,
          orig_date, date);
d404 1
a404 12
      if (status == FIXERR_NO_CHANGE) status = 0;
    }
  }

  if (*infomsg) {
    wcserr_set(WCSERR_SET(FIXERR_DATE_FIX), infomsg);
  }

  return status;
}

/*--------------------------------------------------------------------------*/
d406 2
a407 31
int obsfix(int ctrl, struct wcsprm *wcs)

{
  static const char *function = "obsfix";

  /* IAU(1976) ellipsoid (as prescribed by WCS Paper VII). */
  const double a = 6378140.0, f = 1.0 / 298.2577;
  const double e2 = (2.0 - f)*f;

  char   *cp, infomsg[256];
  int    havelbh = 7, havexyz = 7, i, k, status;
  double coslat, coslng, d, hgt, lat, lng, n, r2, rho, sinlat, sinlng, x, y,
         z, zeta;
  struct wcserr **err;

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  err = &(wcs->err);

  /* Set masks for checking partially-defined coordinate triplets. */
  havexyz -= 1*undefined(wcs->obsgeo[0]);
  havexyz -= 2*undefined(wcs->obsgeo[1]);
  havexyz -= 4*undefined(wcs->obsgeo[2]);
  havelbh -= 1*undefined(wcs->obsgeo[3]);
  havelbh -= 2*undefined(wcs->obsgeo[4]);
  havelbh -= 4*undefined(wcs->obsgeo[5]);

  if (ctrl == 2) {
    /* Make no changes. */
    if (0 < havexyz && havexyz < 7) {
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Partially undefined Cartesian coordinate triplet");
d410 6
a415 4
    if (0 < havelbh && havelbh < 7) {
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Partially undefined Geodetic coordinate triplet");
    }
d417 7
a423 33
    if (havexyz == 0 || havelbh == 0) {
      return FIXERR_NO_CHANGE;
    }
  }

  if (havexyz == 0 && havelbh == 0) {
    return FIXERR_NO_CHANGE;
  }


  infomsg[0] = '\0';
  status = FIXERR_NO_CHANGE;

  if (havelbh == 7) {
    /* Compute (x,y,z) from (lng,lat,hgt). */
    sincosd(wcs->obsgeo[3], &sinlng, &coslng);
    sincosd(wcs->obsgeo[4], &sinlat, &coslat);
    n = a / sqrt(1.0 - e2*sinlat*sinlat);
    rho = n + wcs->obsgeo[5];

    x = rho*coslng*coslat;
    y = rho*sinlng*coslat;
    z = (rho - n*e2)*sinlat;

    if (havexyz < 7) {
      /* One or more of the Cartesian elements was undefined. */
      status = 0;
      cp = infomsg;

      if (ctrl == 1 || !(havexyz & 1)) {
        wcs->obsgeo[0] = x;
        sprintf(cp, "%s OBSGEO-X to %12.3f from OBSGEO-[LBH]",
          (havexyz & 1) ? "Reset" : "Set", x);
a424 47

      if (ctrl == 1 || !(havexyz & 2)) {
        wcs->obsgeo[1] = y;

        if ((k = strlen(cp))) {
          strcat(cp+k, ".\n");
          cp += k + 2;
        }

        sprintf(cp, "%s OBSGEO-Y to %12.3f from OBSGEO-[LBH]",
          (havexyz & 2) ? "Reset" : "Set", y);
      }

      if (ctrl == 1 || !(havexyz & 4)) {
        wcs->obsgeo[2] = z;
        if ((k = strlen(cp))) {
          strcat(cp+k, ".\n");
          cp += k + 2;
        }

        sprintf(cp, "%s OBSGEO-Z to %12.3f from OBSGEO-[LBH]",
          (havexyz & 4) ? "Reset" : "Set", z);
      }

      wcserr_set(WCSERR_SET(FIXERR_OBSGEO_FIX), infomsg);

      if (havexyz == 0) {
        /* Skip the consistency check. */
        return status;
      }
    }

  } else if (havexyz == 7) {
    /* Compute (lng,lat,hgt) from (x,y,z). */
    x = wcs->obsgeo[0];
    y = wcs->obsgeo[1];
    z = wcs->obsgeo[2];
    r2 = x*x + y*y;

    /* Iterate over the value of zeta. */
    zeta = z;
    for (i = 0; i < 4; i++) {
      rho = sqrt(r2 + zeta*zeta);
      sinlat = zeta / rho;
      n = a / sqrt(1.0 - e2*sinlat*sinlat);

      zeta = z / (1.0 - n*e2/rho);
a425 55

    lng = atan2d(y, x);
    lat = asind(sinlat);
    hgt = rho - n;

    if (havelbh < 7) {
      /* One or more of the Geodetic elements was undefined. */
      status = 0;
      cp = infomsg;

      if (ctrl == 1 || !(havelbh & 1)) {
        wcs->obsgeo[3] = lng;
        sprintf(cp, "%s OBSGEO-L to %12.6f from OBSGEO-[XYZ]",
          (havelbh & 1) ? "Reset" : "Set", lng);
      }

      if (ctrl == 1 || !(havelbh & 2)) {
        wcs->obsgeo[4] = lat;
        if ((k = strlen(cp))) {
          strcat(cp+k, ".\n");
          cp += k + 2;
        }

        sprintf(cp, "%s OBSGEO-B to %12.6f from OBSGEO-[XYZ]",
          (havelbh & 2) ? "Reset" : "Set", lat);
      }

      if (ctrl == 1 || !(havelbh & 4)) {
        wcs->obsgeo[5] = hgt;
        if ((k = strlen(cp))) {
          strcat(cp+k, ".\n");
          cp += k + 2;
        }

        sprintf(cp, "%s OBSGEO-H to %12.3f from OBSGEO-[XYZ]",
          (havelbh & 4) ? "Reset" : "Set", hgt);
      }

      wcserr_set(WCSERR_SET(FIXERR_OBSGEO_FIX), infomsg);

      if (havelbh == 0) {
        /* Skip the consistency check. */
        return status;
      }
    }

    /* Compute (x,y,z) from (lng,lat,hgt) for consistency checking. */
    sincosd(wcs->obsgeo[3], &sinlng, &coslng);
    sincosd(wcs->obsgeo[4], &sinlat, &coslat);
    n = a / sqrt(1.0 - e2*sinlat*sinlat);
    rho = n + wcs->obsgeo[5];

    x = rho*coslng*coslat;
    y = rho*sinlng*coslat;
    z = (rho - n*e2)*sinlat;
d428 3
d432 1
a432 13
  /* Check consistency. */
  r2 = 0.0;
  d = wcs->obsgeo[0] - x;
  r2 += d*d;
  d = wcs->obsgeo[1] - y;
  r2 += d*d;
  d = wcs->obsgeo[2] - z;
  r2 += d*d;

  if (1.0 < r2) {
    d = sqrt(r2);
    return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
      "Observatory coordinates inconsistent by %.1f metres", d);
d435 1
a435 1
  return status;
a437 1

d443 2
a445 3

  int  i, msglen, result, status = FIXERR_NO_CHANGE;
  char orig_unit[72], msg[512], msgtmp[72];
d451 1
a451 1
  strncpy(msg, "Changed units:", 512);
d454 1
a454 1
    strncpy(orig_unit, wcs->cunit[i], 72);
d457 5
a461 5
      msglen = strlen(msg);
      if (msglen < 511) {
        wcsutil_null_fill(72, orig_unit);
        sprintf(msgtmp, "\n  '%s' -> '%s',", orig_unit, wcs->cunit[i]);
        strncpy(msg+msglen, msgtmp, 511-msglen);
d469 2
a470 2
    msglen = strlen(msg) - 2;
    msg[msglen] = '\0';
@


5.19
log
@WCSLIB 5.19
@
text
@d3 1
a3 1
  WCSLIB 5.19 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.18.1.1 2018/01/10 08:33:04 mcalabre Exp mcalabre $
@


5.19.1.1
log
@WCSLIB 5.19 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.19 2018/07/26 15:37:04 mcalabre Exp $
@


5.18
log
@WCSLIB 5.18
@
text
@d3 1
a3 1
  WCSLIB 5.18 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.17.1.2 2018/01/10 08:25:34 mcalabre Exp mcalabre $
@


5.18.1.1
log
@WCSLIB 5.18 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.18 2018/01/10 08:32:14 mcalabre Exp $
@


5.17
log
@WCSLIB 5.17
@
text
@d3 2
a4 2
  WCSLIB 5.17 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2017, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 5.16.1.2 2017/09/12 14:15:32 mcalabre Exp mcalabre $
@


5.17.1.1
log
@WCSLIB 5.17 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.17 2017/09/18 08:44:23 mcalabre Exp $
@


5.17.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2018, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 5.17.1.1 2017/09/18 08:45:59 mcalabre Exp mcalabre $
@


5.16
log
@WCSLIB 5.16
@
text
@d3 1
a3 1
  WCSLIB 5.16 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.15.1.3 2017/01/15 04:13:33 mcalabre Exp mcalabre $
d253 1
a253 1
      wcsutil_str2double(ctmp, "%lf", sec)) {
@


5.16.1.1
log
@WCSLIB 5.16 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.16 2017/01/15 04:25:01 mcalabre Exp $
@


5.16.1.2
log
@Revised argument list to wcsutil_str2double().
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.16.1.1 2017/01/15 04:28:59 mcalabre Exp mcalabre $
d253 1
a253 1
      wcsutil_str2double(ctmp, sec)) {
@


5.15
log
@WCSLIB 5.15
@
text
@d3 2
a4 2
  WCSLIB 5.15 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2016, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 5.14.1.2 2016/03/21 06:10:32 mcalabre Exp mcalabre $
d487 1
a487 1
  int  i, status, velref;
d521 1
a521 1
            wcs->ctype[i], ctype, velref);
@


5.15.1.1
log
@WCSLIB 5.15 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.15 2016/04/05 12:55:10 mcalabre Exp $
@


5.15.1.2
log
@Bug fix for the change at 5.14.1.2.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.15.1.1 2016/04/05 12:58:35 mcalabre Exp mcalabre $
d487 1
a487 1
  int  i, status;
d521 1
a521 1
            wcs->ctype[i], ctype, wcs->velref);
@


5.15.1.3
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2017, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 5.15.1.2 2017/01/15 02:14:17 mcalabre Exp mcalabre $
@


5.14
log
@WCSLIB 5.14
@
text
@d3 1
a3 1
  WCSLIB 5.14 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.13.1.1 2016/01/26 08:54:32 mcalabre Exp mcalabre $
d487 1
a487 1
  int  i, status;
d515 3
a517 2
            "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s'",
            i+1, wcs->ctype[i], ctype, wcs->specsys);
d520 2
a521 1
            "Changed CTYPE%d from '%s' to '%s'", i+1, wcs->ctype[i], ctype);
@


5.14.1.1
log
@WCSLIB 5.14 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.14 2016/02/07 10:49:31 mcalabre Exp $
@


5.14.1.2
log
@In spcfix(), report the value of VELREF if ctype is translated.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.14.1.1 2016/02/07 10:52:44 mcalabre Exp mcalabre $
d487 1
a487 1
  int  i, status, velref;
d515 2
a516 3
            "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s' "
            "(VELREF=%d)", i+1, wcs->ctype[i], ctype, wcs->specsys,
            wcs->velref);
d519 1
a519 2
            "Changed CTYPE%d from '%s' to '%s' (VELREF=%d)", i+1,
            wcs->ctype[i], ctype, velref);
@


5.13
log
@WCSLIB 5.13
@
text
@d3 1
a3 1
  WCSLIB 5.13 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.12.1.3 2016/01/25 12:22:41 mcalabre Exp mcalabre $
@


5.13.1.1
log
@WCSLIB 5.13 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.13 2016/01/26 08:51:13 mcalabre Exp $
@


5.12
log
@WCSLIB 5.12
@
text
@d3 2
a4 2
  WCSLIB 5.12 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2015, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 5.11.1.1 2015/10/18 09:16:27 mcalabre Exp mcalabre $
d444 1
a444 1
  char orig_unit[80], msg[WCSERR_MSG_LENGTH];
d451 2
a452 1
  strcpy(msg, "Changed units: ");
d458 6
a463 2
      sprintf(msg+k, "'%s' -> '%s', ", orig_unit, wcs->cunit[i]);
      status = FIXERR_UNITS_ALIAS;
d468 1
@


5.12.1.1
log
@WCSLIB 5.12 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.12 2015/11/15 02:13:00 mcalabre Exp $
@


5.12.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2016, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 5.12.1.1 2015/11/15 02:16:14 mcalabre Exp mcalabre $
@


5.12.1.3
log
@In unitfix(), ensure that the message buffer cannot be overrun, e.g. by
blank-padded unit strings.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.12.1.2 2016/01/25 05:17:04 mcalabre Exp mcalabre $
d444 1
a444 1
  char orig_unit[80], msg[WCSERR_MSG_LENGTH], msgtmp[WCSERR_MSG_LENGTH];
d451 1
a451 2
  strncpy(msg, "Changed units: ", WCSERR_MSG_LENGTH);

d457 2
a458 6
      if (k < WCSERR_MSG_LENGTH-1) {
        wcsutil_null_fill(80, orig_unit);
        sprintf(msgtmp, "'%s' -> '%s', ", orig_unit, wcs->cunit[i]);
        strncpy(msg+k, msgtmp, WCSERR_MSG_LENGTH-1-k);
        status = FIXERR_UNITS_ALIAS;
      }
a462 1
    /* Chop off the trailing ", ". */
@


5.11
log
@WCSLIB 5.11
@
text
@d3 1
a3 1
  WCSLIB 5.11 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.10.1.1 2015/10/09 08:22:35 mcalabre Exp mcalabre $
@


5.11.1.1
log
@WCSLIB 5.11 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.11 2015/10/18 09:13:05 mcalabre Exp $
@


5.10
log
@WCSLIB 5.10
@
text
@d3 1
a3 1
  WCSLIB 5.10 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.9.1.1 2015/07/21 09:28:07 mcalabre Exp mcalabre $
@


5.10.1.1
log
@WCSLIB 5.10 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.10 2015/10/09 08:19:15 mcalabre Exp $
@


5.9
log
@WCSLIB 5.9
@
text
@d3 1
a3 1
  WCSLIB 5.9 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.8.1.1 2015/07/08 11:07:18 mcalabre Exp mcalabre $
@


5.9.1.1
log
@WCSLIB 5.9 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.9 2015/07/21 09:20:01 mcalabre Exp $
@


5.8
log
@WCSLIB 5.8
@
text
@d3 1
a3 1
  WCSLIB 5.8 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.7.1.1 2015/06/29 02:47:24 mcalabre Exp mcalabre $
@


5.8.1.1
log
@WCSLIB 5.8 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.8 2015/07/08 11:03:59 mcalabre Exp $
@


5.7
log
@WCSLIB 5.7
@
text
@d3 1
a3 1
  WCSLIB 5.7 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.6.1.1 2015/06/14 07:14:44 mcalabre Exp mcalabre $
@


5.7.1.1
log
@WCSLIB 5.7 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.7 2015/06/29 02:44:16 mcalabre Exp $
@


5.6
log
@WCSLIB 5.6
@
text
@d3 1
a3 1
  WCSLIB 5.6 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.5.1.1 2015/05/05 13:21:00 mcalabre Exp mcalabre $
@


5.6.1.1
log
@WCSLIB 5.6 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.6 2015/06/14 07:11:24 mcalabre Exp $
@


5.5
log
@WCSLIB 5.5
@
text
@d3 1
a3 1
  WCSLIB 5.5 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.4.1.1 2015/04/21 14:44:28 mcalabre Exp mcalabre $
@


5.5.1.1
log
@WCSLIB 5.5 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.5 2015/05/05 13:16:31 mcalabre Exp $
@


5.4
log
@WCSLIB 5.4
@
text
@d3 1
a3 1
  WCSLIB 5.4 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.3.1.1 2015/04/21 02:54:02 mcalabre Exp mcalabre $
@


5.4.1.1
log
@WCSLIB 5.4 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.4 2015/04/21 14:41:05 mcalabre Exp $
@


5.3
log
@WCSLIB 5.3
@
text
@d3 1
a3 1
  WCSLIB 5.3 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.2.1.1 2015/04/15 12:39:14 mcalabre Exp mcalabre $
@


5.3.1.1
log
@WCSLIB 5.3 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.3 2015/04/21 02:50:51 mcalabre Exp $
@


5.2
log
@WCSLIB 5.2
@
text
@d3 1
a3 1
  WCSLIB 5.2 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.1.1.2 2015/04/13 12:25:22 mcalabre Exp mcalabre $
@


5.2.1.1
log
@WCSLIB 5.2 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.2 2015/04/15 12:35:07 mcalabre Exp $
@


5.1
log
@WCSLIB 5.1
@
text
@d3 1
a3 1
  WCSLIB 5.1 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 5.0.1.1 2015/04/05 12:28:00 mcalabre Exp mcalabre $
d567 1
a567 1
        /* Allocate space for two more PVi_ja keyvalues. */
d615 1
a615 1
          /* Allocate space for three more PVi_ja keyvalues. */
@


5.1.1.1
log
@WCSLIB 5.1 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.1 2015/04/07 10:56:36 mcalabre Exp $
@


5.1.1.2
log
@Fixed a typos in a couple of comments.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.1.1.1 2015/04/07 11:01:10 mcalabre Exp mcalabre $
d567 1
a567 1
        /* Allocate space for two more PVi_ma keyvalues. */
d615 1
a615 1
          /* Allocate space for three more PVi_ma keyvalues. */
@


5.0
log
@WCSLIB 5.0
@
text
@d3 1
a3 1
  WCSLIB 5.0 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.25.1.3 2015/04/03 01:36:54 mcalabre Exp mcalabre $
@


5.0.1.1
log
@WCSLIB 5.0 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 5.0 2015/04/05 12:24:59 mcalabre Exp $
@


4.25
log
@WCSLIB 4.25
@
text
@d3 2
a4 2
  WCSLIB 4.25 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2014, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 4.24.1.1 2014/09/18 15:28:09 mcalabre Exp mcalabre $
d36 1
d61 26
d176 1
a176 1
    } else if (stat[ifix] == FIXERR_SUCCESS) {
d237 1
a237 1
    status = FIXERR_SUCCESS;
d432 1
a432 1
    return FIXERR_SUCCESS;
d467 1
a467 1
    status = FIXERR_SUCCESS;
d499 1
a499 1
        status = FIXERR_SUCCESS;
d506 1
a506 1
        if (status == FIXERR_SUCCESS) {
d514 1
a514 1
          status = FIXERR_SUCCESS;
d521 1
a521 1
      if (status == FIXERR_SUCCESS) {
d556 1
a556 1
    if ((status = wcsset(wcs))) return status;
d599 1
a599 1
      return FIXERR_SUCCESS;
d654 1
a654 1
      return FIXERR_SUCCESS;
d680 1
a680 1
    if ((status = wcsset(wcs))) return status;
d720 1
a720 1
  if (phimin > phimax) return status;
d733 3
a735 1
      return wcserr_set(WCSFIX_ERRMSG(FIXERR_BAD_PARAM));
d737 1
a737 1
    return wcserr_set(WCSFIX_ERRMSG(FIXERR_NO_REF_PIX_COORD));
d747 1
a747 1
    return wcserr_set(WCSFIX_ERRMSG(status));
d754 1
a754 4
    if (wcs->err->status == WCSERR_BAD_PIX) {
      wcs->err->status = FIXERR_NO_REF_PIX_COORD;
    }
    return wcs->err->status;
@


4.25.1.1
log
@WCSLIB 4.25 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.25 2014/12/14 14:29:36 mcalabre Exp $
@


4.25.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2015, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 4.25.1.1 2014/12/14 14:32:45 mcalabre Exp mcalabre $
@


4.25.1.3
log
@Created fix_linerr[] and fix_wcserr[] to map status returns from the lin and
wcs routines.  Return a successful status directly as 0 rather than the
long-winded FIXERR_SUCCESS.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.25.1.2 2015/01/06 01:01:06 mcalabre Exp mcalabre $
a35 1
#include "lin.h"
a59 26
/* Map error returns for lower-level routines. */
const int fix_linerr[] = {
  FIXERR_SUCCESS,		/*  0: LINERR_SUCCESS         */
  FIXERR_NULL_POINTER,		/*  1: LINERR_NULL_POINTER    */
  FIXERR_MEMORY,		/*  2: LINERR_MEMORY          */
  FIXERR_SINGULAR_MTX,		/*  3: LINERR_SINGULAR_MTX    */
  FIXERR_BAD_PARAM,		/*  4: LINERR_DISTORT_INIT    */
  FIXERR_NO_REF_PIX_COORD,	/*  5: LINERR_DISTORT         */
  FIXERR_NO_REF_PIX_VAL		/*  6: LINERR_DEDISTORT       */
};

const int fix_wcserr[] = {
  FIXERR_SUCCESS,		/*  0: WCSERR_SUCCESS         */
  FIXERR_NULL_POINTER,		/*  1: WCSERR_NULL_POINTER    */
  FIXERR_MEMORY,		/*  2: WCSERR_MEMORY          */
  FIXERR_SINGULAR_MTX,		/*  3: WCSERR_SINGULAR_MTX    */
  FIXERR_BAD_CTYPE,		/*  4: WCSERR_BAD_CTYPE       */
  FIXERR_BAD_PARAM,		/*  5: WCSERR_BAD_PARAM       */
  FIXERR_BAD_COORD_TRANS,	/*  6: WCSERR_BAD_COORD_TRANS */
  FIXERR_ILL_COORD_TRANS,	/*  7: WCSERR_ILL_COORD_TRANS */
  FIXERR_BAD_CORNER_PIX,	/*  8: WCSERR_BAD_PIX         */
  FIXERR_NO_REF_PIX_VAL,	/*  9: WCSERR_BAD_WORLD       */
  FIXERR_NO_REF_PIX_VAL 	/* 10: WCSERR_BAD_WORLD_COORD */
				/*     ...others not used     */
};

d149 1
a149 1
    } else if (stat[ifix] == 0) {
d210 1
a210 1
    status = 0;
d405 1
a405 1
    return 0;
d440 1
a440 1
    status = 0;
d472 1
a472 1
        status = 0;
d479 1
a479 1
        if (status == 0) {
d487 1
a487 1
          status = 0;
d494 1
a494 1
      if (status == 0) {
d529 1
a529 1
    if ((status = wcsset(wcs))) return fix_wcserr[status];
d572 1
a572 1
      return 0;
d627 1
a627 1
      return 0;
d653 1
a653 1
    if ((status = wcsset(wcs))) return fix_wcserr[status];
d693 1
a693 1
  if (phimin > phimax) return fix_wcserr[status];
d706 1
a706 3
      status = FIXERR_BAD_PARAM;
    } else {
      status = FIXERR_NO_REF_PIX_COORD;
d708 1
a708 1
    return wcserr_set(WCSFIX_ERRMSG(status));
d718 1
a718 1
    return wcserr_set(WCSFIX_ERRMSG(fix_linerr[status]));
d725 4
a728 1
    return fix_wcserr[status];
@


4.24
log
@WCSLIB 4.24
@
text
@d3 1
a3 1
  WCSLIB 4.24 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.23.1.1 2014/05/13 05:53:49 mcalabre Exp mcalabre $
@


4.24.1.1
log
@WCSLIB 4.24 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.24 2014/09/18 15:25:00 mcalabre Exp $
@


4.23
log
@WCSLIB 4.23
@
text
@d3 1
a3 1
  WCSLIB 4.23 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.22.1.1 2014/04/12 15:06:32 mcalabre Exp mcalabre $
@


4.23.1.1
log
@WCSLIB 4.23 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.23 2014/05/13 05:50:51 mcalabre Exp $
@


4.22
log
@WCSLIB 4.22
@
text
@d3 1
a3 1
  WCSLIB 4.22 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.21.1.1 2014/03/24 05:15:28 mcalabre Exp mcalabre $
@


4.22.1.1
log
@WCSLIB 4.22 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.22 2014/04/12 15:03:52 mcalabre Exp $
@


4.21
log
@WCSLIB 4.21
@
text
@d3 1
a3 1
  WCSLIB 4.21 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.20.1.2 2014/03/21 06:32:19 mcalabre Exp mcalabre $
@


4.21.1.1
log
@WCSLIB 4.21 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.21 2014/03/24 05:12:27 mcalabre Exp $
@


4.20
log
@WCSLIB 4.20
@
text
@d3 2
a4 2
  WCSLIB 4.20 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2013, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 4.19.1.2 2013/12/17 15:01:43 mcalabre Exp mcalabre $
@


4.20.1.1
log
@WCSLIB 4.20 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.20 2013/12/18 05:42:49 mcalabre Exp $
@


4.20.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2014, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 4.20.1.1 2013/12/18 05:46:55 mcalabre Exp mcalabre $
@


4.19
log
@WCSLIB 4.19
@
text
@d3 1
a3 1
  WCSLIB 4.19 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.18.1.1 2013/07/13 10:02:37 mcalabre Exp mcalabre $
d416 1
a416 1
  int  i, k, status = FIXERR_NO_CHANGE;
d427 2
a428 1
    if (wcsutrne(ctrl, wcs->cunit[i], &(wcs->err)) == 0) {
@


4.19.1.1
log
@WCSLIB 4.19 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.19 2013/09/29 14:17:51 mcalabre Exp $
@


4.19.1.2
log
@Also report potentially unsafe units translations in unitfix() (reported
by Michael Droettboom).
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.19.1.1 2013/09/29 14:20:38 mcalabre Exp mcalabre $
d416 1
a416 1
  int  i, k, result, status = FIXERR_NO_CHANGE;
d427 1
a427 2
    result = wcsutrne(ctrl, wcs->cunit[i], &(wcs->err));
    if (result == 0 || result == 12) {
@


4.18
log
@WCSLIB 4.18
@
text
@d3 1
a3 1
  WCSLIB 4.18 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.17.1.2 2013/06/10 03:48:15 mcalabre Exp mcalabre $
@


4.18.1.1
log
@WCSLIB 4.18 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.18 2013/07/13 10:00:04 mcalabre Exp $
@


4.17
log
@WCSLIB 4.17
@
text
@d3 1
a3 1
  WCSLIB 4.17 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.16.1.3 2013/01/29 05:09:44 cal103 Exp cal103 $
d586 1
a586 1
        if (wcs->npvmax < wcs->npv + 2) {
@


4.17.1.1
log
@WCSLIB 4.17 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.17 2013/01/29 05:29:20 cal103 Exp $
@


4.17.1.2
log
@Fixed the test for the requirement to create three more pv parameters when
translating GLS to SFL.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.17.1.1 2013/01/29 05:32:32 cal103 Exp cal103 $
d586 1
a586 1
        if (wcs->npvmax < wcs->npv + 3) {
@


4.16
log
@WCSLIB 4.16
@
text
@d3 2
a4 2
  WCSLIB 4.16 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2012, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 4.15.1.1 2012/09/26 14:27:51 cal103 Exp cal103 $
a247 1
  char ctmp[72], ctmp2[72];
@


4.16.1.1
log
@WCSLIB 4.16 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.16 2012/11/07 04:42:44 cal103 Exp $
@


4.16.1.2
log
@Removed unused variables from datfix().
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.16.1.1 2012/11/07 04:45:56 cal103 Exp cal103 $
d248 1
@


4.16.1.3
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2013, Mark Calabretta
d25 1
a25 1
  $Id: wcsfix.c,v 4.16.1.2 2012/11/28 01:24:26 cal103 Exp cal103 $
@


4.15
log
@WCSLIB 4.15
@
text
@d3 1
a3 1
  WCSLIB 4.15 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.14.1.1 2012/07/13 10:04:44 cal103 Exp cal103 $
@


4.15.1.1
log
@WCSLIB 4.15 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.15 2012/09/26 14:26:05 cal103 Exp $
@


4.14
log
@WCSLIB 4.14
@
text
@d3 1
a3 1
  WCSLIB 4.14 - an implementation of the FITS WCS standard.
d25 1
a25 1
  $Id: wcsfix.c,v 4.13.1.4 2012/07/13 08:50:01 cal103 Exp cal103 $
@


4.14.1.1
log
@WCSLIB 4.14 development branch.
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $
@


4.13
log
@WCSLIB 4.13
@
text
@d3 1
a3 1
  WCSLIB 4.13 - an implementation of the FITS WCS standard.
d19 1
a19 1
  along with WCSLIB.  If not, see <http://www.gnu.org/licenses/>.
d21 5
a25 11
  Correspondence concerning WCSLIB may be directed to:
    Internet email: mcalabre@@atnf.csiro.au
    Postal address: Dr. Mark Calabretta
                    Australia Telescope National Facility, CSIRO
                    PO Box 76
                    Epping NSW 1710
                    AUSTRALIA

  Author: Mark Calabretta, Australia Telescope National Facility
  http://www.atnf.csiro.au/~mcalabre/index.html
  $Id: wcsfix.c,v 4.12.1.1 2012/02/29 07:59:10 cal103 Exp cal103 $
d220 22
d248 1
d315 1
a315 1
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) {
d323 3
a325 1
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) {
a326 2
        } else {
          sprintf(dateobs+10, "T%.2d:%.2d:%04.1f", hour, minute, sec);
d338 1
a338 1
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) {
d346 3
a348 1
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) {
a349 2
        } else {
          sprintf(dateobs+10, "T%.2d:%.2d:%04.1f", hour, minute, sec);
@


4.13.1.1
log
@WCSLIB 4.13 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.13 2012/03/14 07:39:30 cal103 Exp $
@


4.13.1.2
log
@In datfix(), handle locales such as fr_FR where a comma is used to delimit
the decimal part of a floating point number.  Patch provided by Michael
Droettboom.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.13.1.1 2012/03/14 07:40:37 cal103 Exp cal103 $
a225 22
static int parse_date(const char *buf, int *hour, int *minute, double *sec)

{
  char ctmp[72];

  if (sscanf(buf, "%2d:%2d:%s", hour, minute, ctmp) < 3 ||
      wcsutil_str2double(ctmp, "%lf", sec)) {
    return 1;
  }

  return 0;
}

static void write_date(char *buf, int hour, int minute, double sec)

{
  char ctmp[72];

  wcsutil_double2str(ctmp, "%04.1f", sec);
  sprintf(buf, "T%.2d:%.2d:%s", hour, minute, ctmp);
}

a231 1
  char ctmp[72], ctmp2[72];
d298 1
a298 1
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
d306 1
a306 1
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
d309 1
a309 1
          write_date(dateobs+10, hour, minute, sec);
d321 1
a321 1
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
d329 1
a329 1
        if (parse_date(dateobs+11, &hour, &minute, &sec)) {
d332 1
a332 1
          write_date(dateobs+10, hour, minute, sec);
@


4.13.1.3
log
@Changed contact details.
@
text
@d19 1
a19 1
  along with WCSLIB.  If not, see http://www.gnu.org/licenses.
d21 11
a31 5
  Direct correspondence concerning WCSLIB to mark@@calabretta.id.au

  Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
  http://www.atnf.csiro.au/people/Mark.Calabretta
  $Id: wcsfix.c,v 4.13.1.2 2012/07/12 15:20:28 cal103 Exp cal103 $
@


4.13.1.4
log
@Bug fix in datfix() relating to the return status from parse_date().
@
text
@d25 1
a25 1
  $Id: wcsfix.c,v 4.13.1.3 2012/07/13 03:34:20 cal103 Exp cal103 $
d324 2
a326 2
        } else {
          dateobs[10] = 'T';
d347 2
a349 2
        } else {
          dateobs[10] = 'T';
@


4.12
log
@WCSLIB 4.12
@
text
@d3 1
a3 1
  WCSLIB 4.12 - an implementation of the FITS WCS standard.
d31 1
a31 1
  $Id: wcsfix.c,v 4.11.1.1 2012/02/20 23:31:13 cal103 Exp cal103 $
@


4.12.1.1
log
@WCSLIB 4.12 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.12 2012/02/29 07:57:56 cal103 Exp $
@


4.11
log
@WCSLIB 4.11
@
text
@d3 1
a3 1
  WCSLIB 4.11 - an implementation of the FITS WCS standard.
d31 1
a31 1
  $Id: wcsfix.c,v 4.10.1.1 2012/02/05 23:42:46 cal103 Exp cal103 $
@


4.11.1.1
log
@WCSLIB 4.11 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.11 2012/02/20 23:30:02 cal103 Exp $
@


4.10
log
@WCSLIB 4.10
@
text
@d3 1
a3 1
  WCSLIB 4.10 - an implementation of the FITS WCS standard.
d31 1
a31 1
  $Id: wcsfix.c,v 4.9.1.3 2012/02/02 08:23:24 cal103 Exp cal103 $
@


4.10.1.1
log
@WCSLIB 4.10 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.10 2012/02/05 23:41:44 cal103 Exp $
@


4.9
log
@WCSLIB 4.9
@
text
@d3 1
a3 1
  WCSLIB 4.9 - an implementation of the FITS WCS standard.
d31 1
a31 1
  $Id: wcsfix.c,v 4.8.1.6 2012/01/24 05:23:06 cal103 Exp cal103 $
d88 1
a88 1
  if ((stat[CELFIX] = celfix(wcs)) > 0) {
d92 1
a92 1
  if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
d117 5
a121 1
  messages, is the one returned. */
d138 3
a143 3
    case SPCFIX:
      stat[ifix] = spcfix(wcs);
      break;
d231 1
d241 1
a279 2

      return FIXERR_SUCCESS;
d385 8
a392 1
  return FIXERR_SUCCESS;
d408 1
a408 1
  strcpy(msg, "Translated units: ");
d431 66
a618 42
int spcfix(struct wcsprm *wcs)

{
  char ctype[9], specsys[9];
  int  i, status;

  /* Initialize if required. */
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  if (wcs->flag != WCSSET) {
    if ((status = wcsset(wcs))) return status;
  }

  if ((i = wcs->spec) < 0) {
    /* Look for a linear spectral axis. */
    for (i = 0; i < wcs->naxis; i++) {
      if (wcs->types[i]/100 == 30) {
        break;
      }
    }

    if (i >= wcs->naxis) {
      /* No spectral axis. */
      return FIXERR_NO_CHANGE;
    }
  }

  /* Translate an AIPS-convention spectral type if present. */
  if ((status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys))) {
    return status;
  }

  strcpy(wcs->ctype[i], ctype);
  if (wcs->specsys[1] == '\0') strcpy(wcs->specsys, specsys);

  wcsutil_null_fill(72, wcs->ctype[i]);
  wcsutil_null_fill(72, wcs->specsys);

  return FIXERR_SUCCESS;
}

/*--------------------------------------------------------------------------*/

@


4.9.1.1
log
@WCSLIB 4.9 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.9 2012/01/24 05:40:29 cal103 Exp $
@


4.9.1.2
log
@Changes from Michael Droettboom (slightly modified) for returning
informative messages from datfix() and spcfix().  If spcfix() is to
return an informative message, wcsset() cannot precede it as it also
translates AIPS-convention spectral axes (for the benefit of spctyp()).
Thus, modified spcfix() so that it doesn't call wcsset(), and changed
the order of execution of sub-fixers in wcsfix() and wcsfixi() so that
spcfix() precedes celfix() and cylfix() which both necessarily invoke
wcsset().
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.9.1.1 2012/01/24 05:41:45 cal103 Exp cal103 $
d88 1
a88 1
  if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
d92 1
a92 1
  if ((stat[CELFIX] = celfix(wcs)) > 0) {
d117 1
a117 5
  messages, is the one returned.

  To get informative messages from spcfix() it must precede celfix() and
  cylfix().  The latter call wcsset() which also translates AIPS-convention
  spectral axes. */
d134 3
a139 3
    case CELFIX:
      stat[ifix] = celfix(wcs);
      break;
a226 1
  char orig_dateobs[72];
a235 1
  strncpy(orig_dateobs, dateobs, 72);
d274 2
d381 1
a381 8
  if (strncmp(orig_dateobs, dateobs, 72)) {
    wcserr_set(WCSERR_SET(FIXERR_DATE_FIX),
      "Changed '%s' to '%s'", orig_dateobs, dateobs);

    return FIXERR_SUCCESS;
  }

  return FIXERR_NO_CHANGE;
d397 1
a397 1
  strcpy(msg, "Changed units: ");
a419 41
int spcfix(struct wcsprm *wcs)

{
  static const char *function = "spcfix";

  char ctype[9], specsys[9];
  int  i, status;
  struct wcserr **err;

  if (wcs == 0x0) return FIXERR_NULL_POINTER;
  err = &(wcs->err);

  for (i = 0; i < wcs->naxis; i++) {
    /* Translate an AIPS-convention spectral type if present. */
    if (spcaips(wcs->ctype[i], wcs->velref, ctype, specsys) == 0) {
      if (wcs->specsys[0] == '\0') {
        strncpy(wcs->specsys, specsys, 9);
        wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
          "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s'",
          i+1, wcs->ctype[i], ctype, wcs->specsys);
      } else {
        wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
          "Changed CTYPE%d from '%s' to '%s'",
          i+1, wcs->ctype[i], ctype);
      }

      strcpy(wcs->ctype[i], ctype);

      wcsutil_null_fill(72, wcs->ctype[i]);
      wcsutil_null_fill(72, wcs->specsys);

      /* Exit now, wcsset() will fail if there are multiple spectral axes. */
      return FIXERR_SUCCESS;
    }
  }

  return FIXERR_NO_CHANGE;
}

/*--------------------------------------------------------------------------*/

d542 42
@


4.9.1.3
log
@Tuned spcfix() starting with corrections from Michael Droettboom.  It
now also handles SPCERR_BAD_SPEC_PARAMS returned from spcaips().
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.9.1.2 2012/02/01 00:09:53 cal103 Exp cal103 $
d445 2
a446 7
    status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys);
    if (status == 0) {
      /* An AIPS type was found but it may match what we already have. */
      status = FIXERR_NO_CHANGE;

      /* Was specsys translated? */
      if (wcs->specsys[0] == '\0' && *specsys) {
d449 6
a454 2
          "Changed SPECSYS to '%s'", specsys);
        status = FIXERR_SUCCESS;
d457 1
a457 14
      /* Was ctype translated?  Have to null-fill for comparing them. */
      wcsutil_null_fill(9, wcs->ctype[i]);
      if (strncmp(wcs->ctype[i], ctype, 9)) {
        /* ctype was translated... */
        if (status == FIXERR_SUCCESS) {
          /* ...and specsys was also. */
          wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
            "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s'",
            i+1, wcs->ctype[i], ctype, wcs->specsys);
        } else {
          wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE),
            "Changed CTYPE%d from '%s' to '%s'", i+1, wcs->ctype[i], ctype);
          status = FIXERR_SUCCESS;
        }
d459 2
a460 2
        strncpy(wcs->ctype[i], ctype, 9);
      }
d462 2
a463 13
      /* Tidy up. */
      if (status == FIXERR_SUCCESS) {
        wcsutil_null_fill(72, wcs->ctype[i]);
        wcsutil_null_fill(72, wcs->specsys);
      }

      /* No need to check for others, wcsset() will fail if so. */
      return status;

    } else if (status == SPCERR_BAD_SPEC_PARAMS) {
      /* An AIPS spectral type was found but with invalid velref. */
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
        "Invalid parameter value: velref = %d", wcs->velref);
@


4.8
log
@WCSLIB 4.8
@
text
@d3 2
a4 2
  WCSLIB 4.8 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2011, Mark Calabretta
d31 1
a31 1
  $Id: wcsfix.c,v 4.7.1.4 2011/08/10 05:50:57 cal103 Exp cal103 $
d96 2
a97 6
  if (naxis) {
    if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
      status = 1;
    }
  } else {
    stat[CYLFIX] = -2;
d109 2
a110 2
  int status = 0;
  struct wcserr *err;
d112 46
a157 7
  err = info + CDFIX;
  if ((stat[CDFIX] = cdfix(wcs)) > 0) {
    wcserr_copy(wcs->err, err);
    status = 1;
  } else {
    wcserr_copy(0x0, err);
  }
d159 3
a161 7
  err = info + DATFIX;
  if ((stat[DATFIX] = datfix(wcs)) > 0) {
    wcserr_copy(wcs->err, err);
    status = 1;
  } else {
    wcserr_copy(0x0, err);
  }
d163 5
a167 6
  err = info + UNITFIX;
  if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) {
    wcserr_copy(wcs->err, err);
    status = 1;
  } else {
    wcserr_copy(0x0, err);
d170 3
a172 12
  err = info + CELFIX;
  if ((stat[CELFIX] = celfix(wcs)) > 0) {
    wcserr_copy(wcs->err, err);
    status = 1;
  } else {
    wcserr_copy(0x0, err);
  }

  err = info + SPCFIX;
  if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
    wcserr_copy(wcs->err, err);
    status = 1;
d174 1
a174 1
    wcserr_copy(0x0, err);
a176 15
  err = info + CYLFIX;
  wcserr_copy(0x0, err);
  if (naxis) {
    if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
      err = info + CYLFIX;
      wcserr_copy(wcs->err, err);
      status = 1;
    }
  } else {
    stat[CYLFIX] = -2;
  }

  if (wcs->err) free(wcs->err);
  wcs->err = 0x0;

d299 3
d305 1
a305 3
          hour = 0;
          minute = 0;
          sec = 0.0;
d322 3
d328 1
a328 3
          hour = 0;
          minute = 0;
          sec = 0.0;
d389 4
a392 1
  int  i, status = FIXERR_NO_CHANGE;
d395 1
d397 1
d399 1
d401 3
a403 1
      status = FIXERR_SUCCESS;
d407 8
d579 1
a579 1
  return 0;
d595 1
@


4.8.1.1
log
@WCSLIB 4.8 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.8 2011/08/15 08:05:53 cal103 Exp $
@


4.8.1.2
log
@Reworked wcsfixi() to preserve informative messages in wcserr::msg
associated with negative status returns, and modified unitfix() to
return an informative message if any alias translations are done.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.8.1.1 2011/08/15 08:07:06 cal103 Exp cal103 $
d114 1
d116 1
d118 1
d120 2
a122 1
  wcserr_copy(wcs->err, info+CDFIX);
d124 1
d126 1
d128 2
a130 1
  wcserr_copy(wcs->err, info+DATFIX);
d132 1
d134 1
d136 2
a138 1
  wcserr_copy(wcs->err, info+UNITFIX);
d140 1
d142 1
d144 2
a146 1
  wcserr_copy(wcs->err, info+CELFIX);
d148 1
d150 1
d152 2
a154 1
  wcserr_copy(wcs->err, info+SPCFIX);
d156 2
d160 2
a163 1
    wcserr_copy(wcs->err, info+CYLFIX);
a165 1
    wcserr_copy(0x0, info+CYLFIX);
d381 1
a381 4
  int  i, k, status = FIXERR_NO_CHANGE;
  char orig_unit[80], msg[WCSERR_MSG_LENGTH];
  const char *function = "unitfix";
  struct wcserr **err;
a383 1
  err = &(wcs->err);
a384 1
  strcpy(msg, "Translated units: ");
a385 1
    strncpy(orig_unit, wcs->cunit[i], 80);
d387 1
a387 3
      k = strlen(msg);
      sprintf(msg+k, "'%s' -> '%s', ", orig_unit, wcs->cunit[i]);
      status = FIXERR_UNITS_ALIAS;
a390 8
  if (status == FIXERR_UNITS_ALIAS) {
    k = strlen(msg) - 2;
    msg[k] = '\0';
    wcserr_set(WCSERR_SET(FIXERR_UNITS_ALIAS), msg);

    status = FIXERR_SUCCESS;
  }

d555 1
a555 1
  return FIXERR_SUCCESS;
@


4.8.1.3
log
@Fixes to wcsfixi() for collecting the messages properly in the info
array (from Michael Droettboom).
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.8.1.2 2011/11/17 03:19:58 cal103 Exp cal103 $
d101 1
a101 1
    stat[CYLFIX] = FIXERR_NO_CHANGE;
d115 2
a116 8
  /* Remember that the sub-fixers might generate negative status values for
     informative (not error) messages. */

  if ((stat[CDFIX] = cdfix(wcs))) {
    wcserr_copy(wcs->err, info+CDFIX);
    status = stat[CDFIX] > 0;
  } else {
    wcserr_copy(0x0, info+CDFIX);
d118 1
d120 2
a121 5
  if ((stat[DATFIX] = datfix(wcs))) {
    wcserr_copy(wcs->err, info+DATFIX);
    status = stat[DATFIX] > 0;
  } else {
    wcserr_copy(0x0, info+DATFIX);
d123 1
d125 2
a126 5
  if ((stat[UNITFIX] = unitfix(ctrl, wcs))) {
    wcserr_copy(wcs->err, info+UNITFIX);
    status = stat[UNITFIX] > 0;
  } else {
    wcserr_copy(0x0, info+UNITFIX);
d128 1
d130 2
a131 5
  if ((stat[CELFIX] = celfix(wcs))) {
    wcserr_copy(wcs->err, info+CELFIX);
    status = stat[CELFIX] > 0;
  } else {
    wcserr_copy(0x0, info+CELFIX);
d133 1
d135 2
a136 5
  if ((stat[SPCFIX] = spcfix(wcs))) {
    wcserr_copy(wcs->err, info+SPCFIX);
    status = stat[SPCFIX] > 0;
  } else {
    wcserr_copy(0x0, info+SPCFIX);
d138 1
d141 2
a142 5
    if ((stat[CYLFIX] = cylfix(naxis, wcs))) {
      wcserr_copy(wcs->err, info+CYLFIX);
      status = stat[CYLFIX] > 0;
    } else {
      wcserr_copy(0x0, info+CYLFIX);
d144 1
d146 1
a146 1
    stat[CYLFIX] = FIXERR_NO_CHANGE;
d150 3
@


4.8.1.4
log
@Another go at fixing wcsfixi().
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.8.1.3 2012/01/19 05:09:30 cal103 Exp cal103 $
d96 6
a101 2
  if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
    status = 1;
d113 18
a130 2
  int ifix, status = 0;
  struct wcserr err;
d132 6
a137 46
  /* Handling the status values returned from the sub-fixers is trickier than
  it might seem, especially considering that wcs->err may contain an error
  status on input which should be preserved if no translation errors occur.
  The simplest way seems to be to save a copy of wcs->err and clear it before
  each sub-fixer.  The last real error to occur, excluding informative
  messages, is the one returned. */
  wcserr_copy(wcs->err, &err);

  for (ifix = CDFIX; ifix < NWCSFIX; ifix++) {
    /* Clear wcs->err. */
    wcserr_clear(&(wcs->err));

    switch (ifix) {
    case CDFIX:
      stat[ifix] = cdfix(wcs);
      break;
    case DATFIX:
      stat[ifix] = datfix(wcs);
      break;
    case UNITFIX:
      stat[ifix] = unitfix(ctrl, wcs);
      break;
    case CELFIX:
      stat[ifix] = celfix(wcs);
      break;
    case SPCFIX:
      stat[ifix] = spcfix(wcs);
      break;
    case CYLFIX:
      stat[ifix] = cylfix(naxis, wcs);
      break;
    default:
      continue;
    }

    if (stat[ifix] == FIXERR_NO_CHANGE) {
      /* No change => no message. */
      wcserr_copy(0x0, info+ifix);

    } else if (stat[ifix] == FIXERR_SUCCESS) {
      /* Successful translation, but there may be an informative message. */
      if (wcs->err && wcs->err->status < 0) {
        wcserr_copy(wcs->err, info+ifix);
      } else {
        wcserr_copy(0x0, info+ifix);
      }
d139 6
a144 3
    } else {
      /* An informative message or error message. */
      wcserr_copy(wcs->err, info+ifix);
d146 5
a150 5
      if ((status = (stat[ifix] > 0))) {
        /* It was an error, replace the previous one. */
        wcserr_copy(wcs->err, &err);
      }
    }
d153 7
a159 3
  /* Restore the last error to occur. */
  if (err.status) {
    wcserr_copy(&err, wcs->err);
d161 2
a162 1
    wcserr_clear(&(wcs->err));
a580 1
  if (naxis == 0x0) return FIXERR_NO_CHANGE;
@


4.8.1.5
log
@Handle certain malformed date strings more gracefully in datfix().
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.8.1.4 2012/01/23 06:10:06 cal103 Exp cal103 $
d121 1
a121 1
    /* Clear (delete) wcs->err. */
a298 3
        hour = 0;
        minute = 0;
        sec = 0.0;
d302 3
a304 1
          sprintf(dateobs+10, "T%.2d:%.2d:%04.1f", hour, minute, sec);
a320 3
        hour = 0;
        minute = 0;
        sec = 0.0;
d324 3
a326 1
          sprintf(dateobs+10, "T%.2d:%.2d:%04.1f", hour, minute, sec);
@


4.8.1.6
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2012, Mark Calabretta
d31 1
a31 1
  $Id: wcsfix.c,v 4.8.1.5 2012/01/23 06:50:47 cal103 Exp cal103 $
@


4.7
log
@WCSLIB 4.7
@
text
@d3 1
a3 1
  WCSLIB 4.7 - an implementation of the FITS WCS standard.
d31 1
a31 1
  $Id: wcsfix.c,v 4.6.1.2 2011/01/14 06:33:16 cal103 Exp cal103 $
d39 1
d66 3
d109 67
d179 1
a179 1
  int  i, k, naxis, status = -1;
d182 1
a182 1
  if (wcs == 0x0) return 1;
d186 1
a186 1
    return -1;
d190 1
a190 1
  status = -1;
d206 1
a206 1
    status = 0;
d219 2
d224 1
d226 2
a227 1
  if (wcs == 0x0) return 1;
d233 1
a233 1
      return -1;
d269 1
a269 1
      return 0;
d275 2
a276 1
      return 5;
d283 2
a284 1
        return 5;
d289 2
a290 1
          return 5;
d305 2
a306 1
        return 5;
d311 2
a312 1
          return 5;
d332 2
a333 1
          return 5;
d339 2
a340 1
          return 5;
d345 2
a346 1
        return 5;
d367 2
a368 1
        return 5;
d373 1
a373 1
  return 0;
d381 1
a381 1
  int  i, status = -1;
d383 1
a383 1
  if (wcs == 0x0) return 1;
d386 3
a388 1
    if (wcsutrn(ctrl, wcs->cunit[i]) == 0) status = 0;
d399 2
d404 4
a409 1
  if (wcs == 0x0) return 1;
d426 1
a426 1
            return 2;
d440 1
a440 1
          return 2;
d454 1
a454 1
      return 0;
d474 1
a474 1
              return 2;
d488 1
a488 1
            return 2;
d509 1
a509 1
      return 0;
d513 1
a513 1
  return -1;
d525 1
a525 1
  if (wcs == 0x0) return 1;
d540 1
a540 1
      return -1;
d563 2
d569 4
a574 1
  if (wcs == 0x0) return 1;
d580 2
a581 2
  if (wcs->cel.prj.category != CYLINDRICAL) return -1;
  if (wcs->naxis < 2) return -1;
a590 1
  status = 0;
d619 1
a619 1
  if (phimin >= -180.0 && phimax <= 180.0) return -1;
d628 4
a631 1
    return (status == 2) ? 5 : 9;
d641 1
a641 1
    return status;
d648 4
a651 1
    return status == 8 ? 10 : status;
@


4.7.1.1
log
@WCSLIB 4.7 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.7 2011/02/07 07:03:42 cal103 Exp $
@


4.7.1.2
log
@Added error messaging based on wcserr.  Changes supplied by Michael
Droettboom (STScI) with respect to the released WCSLIB 4.7 sources.
@
text
@a104 51
int wcsfix2(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[],
            struct wcserr info[])

{
  int status = 0, i;

  for (i = 0; i < NWCSFIX; ++i) {
    wcserr_ini(&info[i]);
  }
  
  if ((stat[CDFIX] = cdfix(wcs)) > 0) {
    wcserr_copy(&info[CDFIX], &wcs->err);
    status = 1;
  }

  if ((stat[DATFIX] = datfix(wcs)) > 0) {
    wcserr_copy(&info[DATFIX], &wcs->err);
    status = 1;
  }

  if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) {
    wcserr_copy(&info[UNITFIX], &wcs->err);
    status = 1;
  }

  if ((stat[CELFIX] = celfix(wcs)) > 0) {
    wcserr_copy(&info[CELFIX], &wcs->err);
    status = 1;
  }

  if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
    wcserr_copy(&info[SPCFIX], &wcs->err);
    status = 1;
  }

  if (naxis) {
    if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
      wcserr_copy(&info[CYLFIX], &wcs->err);
      status = 1;
    }
  } else {
    stat[CYLFIX] = -2;
  }

  wcserr_ini(&wcs->err);

  return status;
}

/*--------------------------------------------------------------------------*/

d108 1
a108 1
  int  i, k, naxis, status = FIXERR_NO_CHANGE;
d111 1
a111 1
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
d115 1
a115 1
    return FIXERR_NO_CHANGE;
d119 1
a119 1
  status = FIXERR_NO_CHANGE;
d135 1
a135 1
    status = FIXERR_SUCCESS;
d152 1
a152 1
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
d158 1
a158 1
      return FIXERR_NO_CHANGE;
d194 1
a194 1
      return FIXERR_SUCCESS;
d200 1
a200 3
      return WCSERR_SET(
        &wcs->err, FIXERR_BAD_PARAM,
        "Invalid parameter value: date string too short '%s'", dateobs);
d207 1
a207 3
        return WCSERR_SET(
          &wcs->err, FIXERR_BAD_PARAM,
          "Invalid parameter value: invalid date '%s'", dateobs);
d212 1
a212 3
          return WCSERR_SET(
            &wcs->err, FIXERR_BAD_PARAM,
            "Invalid parameter value: invalid time '%s'", dateobs+11);
d227 1
a227 3
        return WCSERR_SET(
          &wcs->err, FIXERR_BAD_PARAM,
          "Invalid parameter value: invalid date '%s'", dateobs);
d232 1
a232 3
          return WCSERR_SET(
            &wcs->err, FIXERR_BAD_PARAM,
            "Invalid parameter value: invalid time '%s'", dateobs+11);
d252 1
a252 3
          return WCSERR_SET(
            &wcs->err, FIXERR_BAD_PARAM,
            "Invalid parameter value: invalid date '%s'", dateobs);
d258 1
a258 3
          return WCSERR_SET(
            &wcs->err, FIXERR_BAD_PARAM,
            "Invalid parameter value: invalid date '%s'", dateobs);
d263 1
a263 3
        return WCSERR_SET(
          &wcs->err, FIXERR_BAD_PARAM,
          "Invalid parameter value: invalid date '%s'", dateobs);
d284 1
a284 3
        return WCSERR_SET(
          &wcs->err, FIXERR_BAD_PARAM,
          "Invalid parameter value: inconsistent date '%s'", dateobs);
d289 1
a289 1
  return FIXERR_SUCCESS;
d299 1
a299 1
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
d302 1
a302 3
    if (wcsutrn_err(ctrl, wcs->cunit[i], &wcs->err) == 0) {
      status = FIXERR_SUCCESS;
    }
d318 1
a318 1
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
d335 1
a335 1
            return WCSERR_SET(&wcs->err, FIXERR_MEMORY, wcsfix_errmsg[FIXERR_MEMORY]);
d349 1
a349 1
          return WCSERR_SET(&wcs->err, FIXERR_MEMORY, wcsfix_errmsg[FIXERR_MEMORY]);
d363 1
a363 1
      return FIXERR_SUCCESS;
d383 1
a383 1
              return WCSERR_SET(&wcs->err, FIXERR_MEMORY, wcsfix_errmsg[FIXERR_MEMORY]);
d397 1
a397 1
            return WCSERR_SET(&wcs->err, FIXERR_MEMORY, wcsfix_errmsg[FIXERR_MEMORY]);
d418 1
a418 1
      return FIXERR_SUCCESS;
d422 1
a422 1
  return FIXERR_NO_CHANGE;
d434 1
a434 1
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
d449 1
a449 1
      return FIXERR_NO_CHANGE;
d478 1
a478 1
  if (wcs == 0x0) return FIXERR_NULL_POINTER;
d484 2
a485 2
  if (wcs->cel.prj.category != CYLINDRICAL) return FIXERR_NO_CHANGE;
  if (wcs->naxis < 2) return FIXERR_NO_CHANGE;
d524 1
a524 1
  if (phimin >= -180.0 && phimax <= 180.0) return FIXERR_NO_CHANGE;
d533 1
a533 4
    wcserr_copy(&wcs->err, &(wcs->cel.err));
    status = wcs->err.status = (
      (status == PRJERR_BAD_PARAM) ? FIXERR_BAD_PARAM : FIXERR_NO_REF_PIX_COORD);
    return status;
a542 1
    wcserr_copy(&wcs->err, &(wcs->lin.err));
d550 1
a550 3
    status = wcs->err.status = (
      (status == WCSERR_BAD_PIX ? FIXERR_NO_REF_PIX_COORD : status));
    return status;
@


4.7.1.3
log
@Revised implementation of Michael Droettboom's error messaging system.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.7.1.1 2011/02/07 07:04:22 cal103 Exp cal103 $
a38 1
#include "wcserr.h"
a64 3
/* Convenience macro for invoking wcserr_set(). */
#define WCSFIX_ERRMSG(status) WCSERR_SET(status), wcsfix_errmsg[status]

d105 1
a105 1
int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[],
d109 1
a109 2
  int status = 0;
  struct wcserr *err;
d111 4
a114 1
  err = info + CDFIX;
d116 1
a116 1
    wcserr_copy(wcs->err, err);
a117 2
  } else {
    wcserr_copy(0x0, err);
a119 1
  err = info + DATFIX;
d121 1
a121 1
    wcserr_copy(wcs->err, err);
a122 2
  } else {
    wcserr_copy(0x0, err);
a124 1
  err = info + UNITFIX;
d126 1
a126 1
    wcserr_copy(wcs->err, err);
a127 2
  } else {
    wcserr_copy(0x0, err);
a129 1
  err = info + CELFIX;
d131 1
a131 1
    wcserr_copy(wcs->err, err);
a132 2
  } else {
    wcserr_copy(0x0, err);
a134 1
  err = info + SPCFIX;
d136 1
a136 1
    wcserr_copy(wcs->err, err);
a137 2
  } else {
    wcserr_copy(0x0, err);
a139 2
  err = info + CYLFIX;
  wcserr_copy(0x0, err);
d142 1
a142 2
      err = info + CYLFIX;
      wcserr_copy(wcs->err, err);
d149 1
a149 2
  if (wcs->err) free(wcs->err);
  wcs->err = 0x0;
a198 2
  static const char *function = "datfix";

a201 1
  struct wcserr **err;
a203 1
  err = &(wcs->err);
d251 2
a252 1
      return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d260 2
a261 1
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d267 2
a268 1
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d284 2
a285 1
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d291 2
a292 1
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d313 2
a314 1
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d321 2
a322 1
          return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d328 2
a329 1
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d351 2
a352 1
        return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM),
d366 1
a366 1
  int  i, status = FIXERR_NO_CHANGE;
d371 1
a371 1
    if (wcsutrne(ctrl, wcs->cunit[i], &(wcs->err)) == 0) {
a383 2
  static const char *function = "celfix";

a386 1
  struct wcserr **err;
d388 1
a389 3
  err = &(wcs->err);

  /* Initialize if required. */
d406 1
a406 1
            return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
d420 1
a420 1
          return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
d454 1
a454 1
              return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
d468 1
a468 1
            return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY));
a542 2
  static const char *function = "cylfix";

a546 1
  struct wcserr **err;
d548 1
a549 3
  err = &(wcs->err);

  /* Initialize if required. */
d566 1
d583 2
a584 1
    if (!wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0], stat)) {
d592 1
a592 1
  if (phimin > phimax) return wcs->err->status;
d604 4
a607 4
    if (status == PRJERR_BAD_PARAM) {
      return wcserr_set(WCSFIX_ERRMSG(FIXERR_BAD_PARAM));
    }
    return wcserr_set(WCSFIX_ERRMSG(FIXERR_NO_REF_PIX_COORD));
d617 2
a618 1
    return wcserr_set(WCSFIX_ERRMSG(status));
d625 3
a627 4
    if (wcs->err->status == WCSERR_BAD_PIX) {
      wcs->err->status = FIXERR_NO_REF_PIX_COORD;
    }
    return wcs->err->status;
@


4.7.1.4
log
@Fixed the return status for computing coordinates in the four corners.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.7.1.3 2011/08/09 02:43:50 cal103 Exp cal103 $
d607 1
a607 2
    if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0],
                          stat))) {
d615 1
a615 1
  if (phimin > phimax) return status;
@


4.6
log
@WCSLIB 4.6
@
text
@d3 2
a4 2
  WCSLIB 4.6 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2010, Mark Calabretta
d31 1
a31 1
  $Id: wcsfix.c,v 4.5.1.2 2010/09/23 01:32:16 cal103 Exp cal103 $
@


4.6.1.1
log
@WCSLIB 4.6 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.6 2010/11/16 06:15:31 cal103 Exp $
@


4.6.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2011, Mark Calabretta
d31 1
a31 1
  $Id: wcsfix.c,v 4.6.1.1 2010/11/16 06:16:19 cal103 Exp cal103 $
@


4.5
log
@WCSLIB 4.5
@
text
@d3 1
a3 1
  WCSLIB 4.5 - an implementation of the FITS WCS standard.
d31 1
a31 1
  $Id: wcsfix.c,v 4.4.1.4 2010/07/14 07:58:12 cal103 Exp cal103 $
d371 7
a377 5
         * latitude for GLS simply translates the reference point (i.e. the
         * map as a whole) to those coordinates without creating an oblique
         * grid.  This might be effected by adjusting CRPIXja but that is
         * complicated by the linear transformation and instead is
         * accomplished here by setting (phi_0,theta_0). */
d406 1
d409 1
a409 1
        wcs->pv[wcs->npv].value = wcs->crval[wcs->lng];
@


4.5.1.1
log
@WCSLIB 4.5 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.5 2010/07/16 07:01:25 cal103 Exp $
@


4.5.1.2
log
@When translating GLS to SFL, a non-zero reference longitude introduces an
offset in longitude in the normal way, therefore phi_0 = 0 as usual.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.5.1.1 2010/07/16 07:05:31 cal103 Exp cal103 $
d371 5
a375 7
         * latitude for GLS does not create an oblique graticule.  A non-zero
         * reference longitude introduces an offset in longitude in the normal
         * way, whereas a non-zero reference latitude simply translates the
         * reference point (i.e. the map as a whole) to that latitude.  This
         * might be effected by adjusting CRPIXja but that is complicated by
         * the linear transformation and instead is accomplished here by
         * setting theta_0. */
a403 1
        /* Note that the reference longitude is still zero. */
d406 1
a406 1
        wcs->pv[wcs->npv].value = 0.0;
@


4.4
log
@WCSLIB 4.4
@
text
@d3 2
a4 2
  WCSLIB 4.4 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2009, Mark Calabretta
d31 1
a31 1
  $Id: wcsfix.c,v 4.3.1.3 2009/07/28 07:45:25 cal103 Exp cal103 $
d40 1
d427 1
a427 1
  char *scode;
d450 3
a452 11
  /* Was an AIPS-convention spectral type translated? */
  scode = wcs->ctype[i] + 4;

  if (strcmp(scode, "-LSR") == 0) {
    strcpy(wcs->specsys, "LSRK");
  } else if (strcmp(scode, "-HEL") == 0) {
    strcpy(wcs->specsys, "BARYCENT");
  } else if (strcmp(scode, "-OBS") == 0) {
    strcpy(wcs->specsys, "TOPOCENT");
  } else {
    return -1;
d455 2
a456 1
  strcpy(scode, "\0\0\0\0");
d458 2
a459 3
  if (strcmp(wcs->ctype[i], "FELO") == 0) {
    strcpy(wcs->ctype[i], "VOPT-F2W");
  }
@


4.4.1.1
log
@WCSLIB 4.4 development branch.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.4 2009/08/06 07:41:33 cal103 Exp $
@


4.4.1.2
log
@Updated date in copyright notice.
@
text
@d4 1
a4 1
  Copyright (C) 1995-2010, Mark Calabretta
d31 1
a31 1
  $Id: wcsfix.c,v 4.4.1.1 2009/08/10 08:52:49 cal103 Exp cal103 $
@


4.4.1.3
log
@In spcfix(), use spcaips() to translate AIPS spectral types.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.4.1.2 2010/01/19 03:42:50 cal103 Exp cal103 $
a39 1
#include "wcsutil.h"
d426 1
a426 1
  char ctype[9], specsys[9];
d449 11
a459 3
  /* Translate an AIPS-convention spectral type if present. */
  if ((status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys))) {
    return status;
d462 1
a462 2
  strcpy(wcs->ctype[i], ctype);
  if (wcs->specsys[1] = '\0') strcpy(wcs->specsys, specsys);
d464 3
a466 2
  wcsutil_null_fill(72, wcs->ctype[i]);
  wcsutil_null_fill(72, wcs->specsys);
@


4.4.1.4
log
@Bug fix for previous change.
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.4.1.3 2010/07/07 13:34:29 cal103 Exp cal103 $
d456 1
a456 1
  if (wcs->specsys[1] == '\0') strcpy(wcs->specsys, specsys);
@


4.3
log
@WCSLIB 4.3
@
text
@d3 2
a4 2
    WCSLIB 4.3 - an implementation of the FITS WCS standard.
    Copyright (C) 1995-2007, Mark Calabretta
d6 1
a6 1
    This file is part of WCSLIB.
d8 24
a31 24
    WCSLIB is free software: you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License as published by the
    Free Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    WCSLIB 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 Lesser General Public License for
    more details.

    You should have received a copy of the GNU Lesser General Public License
    along with WCSLIB.  If not, see <http://www.gnu.org/licenses/>.

    Correspondence concerning WCSLIB may be directed to:
       Internet email: mcalabre@@atnf.csiro.au
       Postal address: Dr. Mark Calabretta
                       Australia Telescope National Facility, CSIRO
                       PO Box 76
                       Epping NSW 1710
                       AUSTRALIA

    Author: Mark Calabretta, Australia Telescope National Facility
    http://www.atnf.csiro.au/~mcalabre/index.html
    $Id: wcsfix.c,v 4.2.1.4 2007/12/03 05:02:17 cal103 Exp cal103 $
d52 11
a62 11
   "Success",
   "Null wcsprm pointer passed",
   "Memory allocation failed",
   "Linear transformation matrix is singular",
   "Inconsistent or unrecognized coordinate axis types",
   "Invalid parameter value",
   "Invalid coordinate transformation parameters",
   "Ill-conditioned coordinate transformation parameters",
   "All of the corner pixel coordinates are invalid",
   "Could not determine reference pixel coordinate",
   "Could not determine reference pixel value"};
d69 1
a69 1
   int status = 0;
d71 19
a89 15
   if ((stat[CDFIX] = cdfix(wcs)) > 0) {
      status = 1;
   }

   if ((stat[DATFIX] = datfix(wcs)) > 0) {
      status = 1;
   }

   if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) {
      status = 1;
   }

   if ((stat[CELFIX] = celfix(wcs)) > 0) {
      status = 1;
   }
d91 2
a92 1
   if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
d94 4
a97 1
   }
d99 1
a99 9
   if (naxis) {
      if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
         status = 1;
      }
   } else {
      stat[CYLFIX] = -2;
   }

   return status;
d107 2
a108 2
   int  i, k, naxis, status = -1;
   double *cd;
d110 1
a110 6
   if (wcs == 0x0) return 1;

   if ((wcs->altlin & 1) || !(wcs->altlin & 2)) {
      /* Either we have PCi_ja or there are no CDi_ja. */
      return -1;
   }
d112 23
a134 18
   naxis = wcs->naxis;
   status = -1;
   for (i = 0; i < naxis; i++) {
      /* Row of zeros? */
      cd = wcs->cd + i * naxis;
      for (k = 0; k < naxis; k++, cd++) {
         if (*cd != 0.0) goto next;
      }

      /* Column of zeros? */
      cd = wcs->cd + i;
      for (k = 0; k < naxis; k++, cd += naxis) {
         if (*cd != 0.0) goto next;
      }

      cd = wcs->cd + i * (naxis + 1);
      *cd = 1.0;
      status = 0;
d137 1
a137 1
   }
d139 1
a139 1
   return status;
d147 112
a258 11
   char *dateobs;
   int  day, dd, hour = 0, jd, minute = 0, month, msec, n4, year;
   double mjdobs, sec = 0.0, t;

   if (wcs == 0x0) return 1;

   dateobs = wcs->dateobs;
   if (dateobs[0] == '\0') {
      if (undefined(wcs->mjdobs)) {
         /* No date information was provided. */
         return -1;
d261 2
a262 34
         /* Calendar date from MJD. */
         jd = 2400001 + (int)wcs->mjdobs;

         n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
         dd = 10*(((n4-237)%1461)/4) + 5;

         year  = n4/1461 - 4712;
         month = (2 + dd/306)%12 + 1;
         day   = (dd%306)/10 + 1;
         sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day);

         /* Write time part only if non-zero. */
         if ((t = wcs->mjdobs - (int)wcs->mjdobs) > 0.0) {
            t *= 24.0;
            hour = (int)t;
            t = 60.0 * (t - hour);
            minute = (int)t;
            sec    = 60.0 * (t - minute);

            /* Round to 1ms. */
            dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005));
            hour = dd / 3600000;
            dd -= 3600000 * hour;
            minute = dd / 60000;
            msec = dd - 60000 * minute;
            sprintf(dateobs+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);

            /* Write fractions of a second only if non-zero. */
            if (msec%1000) {
               sprintf(dateobs+19, ".%.3d", msec%1000);
            }
         }

         return 0;
d265 1
a265 46
   } else {
      if (strlen(dateobs) < 8) {
         /* Can't be a valid date. */
         return 5;
      }

      /* Identify the date format. */
      if (dateobs[4] == '-' && dateobs[7] == '-') {
         /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */
         if (sscanf(dateobs, "%4d-%2d-%2d", &year, &month, &day) < 3) {
            return 5;
         }

         if (dateobs[10] == 'T') {
            if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) {
               return 5;
            }
         } else if (dateobs[10] == ' ') {
            if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) {
               dateobs[10] = 'T';
            } else {
               hour = 0;
               minute = 0;
               sec = 0.0;
            }
         }

      } else if (dateobs[4] == '/' && dateobs[7] == '/') {
         /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */
         if (sscanf(dateobs, "%4d/%2d/%2d", &year, &month, &day) < 3) {
            return 5;
         }

         if (dateobs[10] == 'T') {
            if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) {
               return 5;
            }
         } else if (dateobs[10] == ' ') {
            if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) {
               dateobs[10] = 'T';
            } else {
               hour = 0;
               minute = 0;
               sec = 0.0;
            }
         }
d267 17
a283 42
         /* Looks ok, fix it up. */
         dateobs[4]  = '-';
         dateobs[7]  = '-';

      } else {
         if (dateobs[2] == '/' && dateobs[5] == '/') {
            /* Old format date: DD/MM/YY, also allowing DD/MM/CCYY. */
            if (sscanf(dateobs, "%2d/%2d/%4d", &day, &month, &year) < 3) {
               return 5;
            }

         } else if (dateobs[2] == '-' && dateobs[5] == '-') {
            /* Also recognize DD-MM-YY and DD-MM-CCYY */
            if (sscanf(dateobs, "%2d-%2d-%4d", &day, &month, &year) < 3) {
               return 5;
            }

         } else {
            /* Not a valid date format. */
            return 5;
         }

         if (year < 100) year += 1900;

         /* Doesn't have a time. */
         sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day);
      }

      /* Compute MJD. */
      mjdobs = (double)((1461*(year - (12-month)/10 + 4712))/4
               + (306*((month+9)%12) + 5)/10
               - (3*((year - (12-month)/10 + 4900)/100))/4
               + day - 2399904)
               + (hour + (minute + sec/60.0)/60.0)/24.0;

      if (undefined(wcs->mjdobs)) {
         wcs->mjdobs = mjdobs;
      } else {
         /* Check for consistency. */
         if (fabs(mjdobs - wcs->mjdobs) > 0.5) {
            return 5;
         }
d285 2
a286 1
   }
d288 1
a288 1
   return 0;
d296 1
a296 1
   int  i, status = -1;
d298 1
a298 1
   if (wcs == 0x0) return 1;
d300 3
a302 3
   for (i = 0; i < wcs->naxis; i++) {
      if (wcsutrn(ctrl, wcs->cunit[i]) == 0) status = 0;
   }
d304 1
a304 1
   return status;
d312 70
a381 31
   int k, status;
   struct celprm *wcscel = &(wcs->cel);
   struct prjprm *wcsprj = &(wcscel->prj);

   /* Initialize if required. */
   if (wcs == 0x0) return 1;
   if (wcs->flag != WCSSET) {
      if ((status = wcsset(wcs))) return status;
   }

   /* Was an NCP or GLS projection code translated? */
   if (wcs->lat >= 0) {
      /* Check ctype. */
      if (strcmp(wcs->ctype[wcs->lat]+5, "NCP") == 0) {
         strcpy(wcs->ctype[wcs->lng]+5, "SIN");
         strcpy(wcs->ctype[wcs->lat]+5, "SIN");

         if (wcs->npvmax < wcs->npv + 2) {
            /* Allocate space for two more PVi_ja keyvalues. */
            if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) {
               if (!(wcs->pv = calloc(wcs->npv+2, sizeof(struct pvcard)))) {
                  wcs->pv = wcs->m_pv;
                  return 2;
               }

               wcs->npvmax = wcs->npv + 2;
               wcs->m_flag = WCSSET;

               for (k = 0; k < wcs->npv; k++) {
                  wcs->pv[k] = wcs->m_pv[k];
               }
d383 2
a384 2
               if (wcs->m_pv) free(wcs->m_pv);
               wcs->m_pv = wcs->pv;
d386 2
a387 2
            } else {
               return 2;
d390 2
a391 6
         }

         wcs->pv[wcs->npv].i = wcs->lat + 1;
         wcs->pv[wcs->npv].m = 1;
         wcs->pv[wcs->npv].value = wcsprj->pv[1];
         (wcs->npv)++;
d393 24
a416 4
         wcs->pv[wcs->npv].i = wcs->lat + 1;
         wcs->pv[wcs->npv].m = 2;
         wcs->pv[wcs->npv].value = wcsprj->pv[2];
         (wcs->npv)++;
d418 1
a418 10
         return 0;

      } else if (strcmp(wcs->ctype[wcs->lat]+5, "GLS") == 0) {
         strcpy(wcs->ctype[wcs->lng]+5, "SFL");
         strcpy(wcs->ctype[wcs->lat]+5, "SFL");
         return 0;
      }
   }

   return -1;
d426 2
a427 2
   char *scode;
   int  i, status;
d429 16
a444 30
   /* Initialize if required. */
   if (wcs == 0x0) return 1;
   if (wcs->flag != WCSSET) {
      if ((status = wcsset(wcs))) return status;
   }

   if ((i = wcs->spec) < 0) {
      /* Look for a linear spectral axis. */
      for (i = 0; i < wcs->naxis; i++) {
         if (wcs->types[i]/100 == 30) {
            break;
         }
      }

      if (i >= wcs->naxis) {
         /* No spectral axis. */
         return -1;
      }
   }

   /* Was an AIPS-convention spectral type translated? */
   scode = wcs->ctype[i] + 4;

   if (strcmp(scode, "-LSR") == 0) {
      strcpy(wcs->specsys, "LSRK");
   } else if (strcmp(scode, "-HEL") == 0) {
      strcpy(wcs->specsys, "BARYCENT");
   } else if (strcmp(scode, "-OBS") == 0) {
      strcpy(wcs->specsys, "TOPOCENT");
   } else {
d446 2
a447 1
   }
d449 2
a450 1
   strcpy(scode, "\0\0\0\0");
d452 15
a466 3
   if (strcmp(wcs->ctype[i], "FELO") == 0) {
      strcpy(wcs->ctype[i], "VOPT-F2W");
   }
d468 1
a468 1
   return 0;
d476 91
a566 91
   unsigned short icnr, indx[NMAX], ncnr;
   int    j, k, stat[4], status;
   double img[4][NMAX], lat, lng, phi[4], phi0, phimax, phimin, pix[4][NMAX],
          *pixj, theta[4], theta0, world[4][NMAX], x, y;

   /* Initialize if required. */
   if (wcs == 0x0) return 1;
   if (wcs->flag != WCSSET) {
      if ((status = wcsset(wcs))) return status;
   }

   /* Check that we have a cylindrical projection. */
   if (wcs->cel.prj.category != CYLINDRICAL) return -1;
   if (wcs->naxis < 2) return -1;


   /* Compute the native longitude in each corner of the image. */
   ncnr = 1 << wcs->naxis;

   for (k = 0; k < NMAX; k++) {
      indx[k] = 1 << k;
   }

   status = 0;
   phimin =  1.0e99;
   phimax = -1.0e99;
   for (icnr = 0; icnr < ncnr;) {
      /* Do four corners at a time. */
      for (j = 0; j < 4; j++, icnr++) {
         pixj = pix[j];

         for (k = 0; k < wcs->naxis; k++) {
            if (icnr & indx[k]) {
               *(pixj++) = naxis[k] + 0.5;
            } else {
               *(pixj++) = 0.5;
            }
         }
      }

      if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta,
                            world[0], stat))) {
         for (j = 0; j < 4; j++) {
            if (phi[j] < phimin) phimin = phi[j];
            if (phi[j] > phimax) phimax = phi[j];
         }
      }
   }

   if (phimin > phimax) return status;

   /* Any changes needed? */
   if (phimin >= -180.0 && phimax <= 180.0) return -1;


   /* Compute the new reference pixel coordinates. */
   phi0 = (phimin + phimax) / 2.0;
   theta0 = 0.0;

   if ((status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y,
                        stat))) {
      return (status == 2) ? 5 : 9;
   }

   for (k = 0; k < wcs->naxis; k++) {
      img[0][k] = 0.0;
   }
   img[0][wcs->lng] = x;
   img[0][wcs->lat] = y;

   if ((status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0]))) {
      return status;
   }


   /* Compute celestial coordinates at the new reference pixel. */
   if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0],
                        stat))) {
      return status == 8 ? 10 : status;
   }

   /* Compute native coordinates of the celestial pole. */
   lng =  0.0;
   lat = 90.0;
   (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng, &lat, phi, theta);

   wcs->crpix[wcs->lng] = pix[0][wcs->lng];
   wcs->crpix[wcs->lat] = pix[0][wcs->lat];
   wcs->crval[wcs->lng] = world[0][wcs->lng];
   wcs->crval[wcs->lat] = world[0][wcs->lat];
   wcs->lonpole = phi[0] - phi0;
d568 1
a568 1
   return wcsset(wcs);
@


4.3.1.1
log
@WCSLIB 4.3 development branch.
@
text
@d3 2
a4 2
    WCSLIB 4.4 - an implementation of the FITS WCS standard.
    Copyright (C) 1995-2008, Mark Calabretta
d31 1
a31 1
    $Id: wcsfix.c,v 4.3 2007/12/27 05:41:36 cal103 Exp cal103 $
@


4.3.1.2
log
@Changed indentation.
@
text
@d3 2
a4 2
  WCSLIB 4.4 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2009, Mark Calabretta
d6 1
a6 1
  This file is part of WCSLIB.
d8 24
a31 24
  WCSLIB is free software: you can redistribute it and/or modify it under the
  terms of the GNU Lesser General Public License as published by the Free
  Software Foundation, either version 3 of the License, or (at your option)
  any later version.

  WCSLIB 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 Lesser General Public License for
  more details.

  You should have received a copy of the GNU Lesser General Public License
  along with WCSLIB.  If not, see <http://www.gnu.org/licenses/>.

  Correspondence concerning WCSLIB may be directed to:
    Internet email: mcalabre@@atnf.csiro.au
    Postal address: Dr. Mark Calabretta
                    Australia Telescope National Facility, CSIRO
                    PO Box 76
                    Epping NSW 1710
                    AUSTRALIA

  Author: Mark Calabretta, Australia Telescope National Facility
  http://www.atnf.csiro.au/~mcalabre/index.html
  $Id: wcsfix.c,v 4.3.1.1 2007/12/27 13:23:47 cal103 Exp cal103 $
d52 11
a62 11
  "Success",
  "Null wcsprm pointer passed",
  "Memory allocation failed",
  "Linear transformation matrix is singular",
  "Inconsistent or unrecognized coordinate axis types",
  "Invalid parameter value",
  "Invalid coordinate transformation parameters",
  "Ill-conditioned coordinate transformation parameters",
  "All of the corner pixel coordinates are invalid",
  "Could not determine reference pixel coordinate",
  "Could not determine reference pixel value"};
d69 1
a69 1
  int status = 0;
d71 7
a77 19
  if ((stat[CDFIX] = cdfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[DATFIX] = datfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) {
    status = 1;
  }

  if ((stat[CELFIX] = celfix(wcs)) > 0) {
    status = 1;
  }

  if ((stat[SPCFIX] = spcfix(wcs)) > 0) {
    status = 1;
  }
d79 1
a79 2
  if (naxis) {
    if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) {
d81 1
a81 4
    }
  } else {
    stat[CYLFIX] = -2;
  }
d83 17
a99 1
  return status;
d107 2
a108 2
  int  i, k, naxis, status = -1;
  double *cd;
d110 6
a115 1
  if (wcs == 0x0) return 1;
d117 18
a134 23
  if ((wcs->altlin & 1) || !(wcs->altlin & 2)) {
    /* Either we have PCi_ja or there are no CDi_ja. */
    return -1;
  }

  naxis = wcs->naxis;
  status = -1;
  for (i = 0; i < naxis; i++) {
    /* Row of zeros? */
    cd = wcs->cd + i * naxis;
    for (k = 0; k < naxis; k++, cd++) {
      if (*cd != 0.0) goto next;
    }

    /* Column of zeros? */
    cd = wcs->cd + i;
    for (k = 0; k < naxis; k++, cd += naxis) {
      if (*cd != 0.0) goto next;
    }

    cd = wcs->cd + i * (naxis + 1);
    *cd = 1.0;
    status = 0;
d137 1
a137 1
  }
d139 1
a139 1
  return status;
d147 18
a164 11
  char *dateobs;
  int  day, dd, hour = 0, jd, minute = 0, month, msec, n4, year;
  double mjdobs, sec = 0.0, t;

  if (wcs == 0x0) return 1;

  dateobs = wcs->dateobs;
  if (dateobs[0] == '\0') {
    if (undefined(wcs->mjdobs)) {
     /* No date information was provided. */
      return -1;
d166 80
a245 100
    } else {
      /* Calendar date from MJD. */
      jd = 2400001 + (int)wcs->mjdobs;

      n4 =  4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37);
      dd = 10*(((n4-237)%1461)/4) + 5;

      year  = n4/1461 - 4712;
      month = (2 + dd/306)%12 + 1;
      day   = (dd%306)/10 + 1;
      sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day);

      /* Write time part only if non-zero. */
      if ((t = wcs->mjdobs - (int)wcs->mjdobs) > 0.0) {
        t *= 24.0;
        hour = (int)t;
        t = 60.0 * (t - hour);
        minute = (int)t;
        sec    = 60.0 * (t - minute);

        /* Round to 1ms. */
        dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005));
        hour = dd / 3600000;
        dd -= 3600000 * hour;
        minute = dd / 60000;
        msec = dd - 60000 * minute;
        sprintf(dateobs+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000);

        /* Write fractions of a second only if non-zero. */
        if (msec%1000) {
          sprintf(dateobs+19, ".%.3d", msec%1000);
        }
      }

      return 0;
    }

  } else {
    if (strlen(dateobs) < 8) {
      /* Can't be a valid date. */
      return 5;
    }

    /* Identify the date format. */
    if (dateobs[4] == '-' && dateobs[7] == '-') {
      /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */
      if (sscanf(dateobs, "%4d-%2d-%2d", &year, &month, &day) < 3) {
        return 5;
      }

      if (dateobs[10] == 'T') {
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) {
          return 5;
        }
      } else if (dateobs[10] == ' ') {
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) {
          dateobs[10] = 'T';
        } else {
          hour = 0;
          minute = 0;
          sec = 0.0;
        }
      }

    } else if (dateobs[4] == '/' && dateobs[7] == '/') {
      /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */
      if (sscanf(dateobs, "%4d/%2d/%2d", &year, &month, &day) < 3) {
        return 5;
      }

      if (dateobs[10] == 'T') {
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) {
          return 5;
        }
      } else if (dateobs[10] == ' ') {
        if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) {
          dateobs[10] = 'T';
        } else {
          hour = 0;
          minute = 0;
          sec = 0.0;
        }
      }

      /* Looks ok, fix it up. */
      dateobs[4]  = '-';
      dateobs[7]  = '-';

    } else {
      if (dateobs[2] == '/' && dateobs[5] == '/') {
        /* Old format date: DD/MM/YY, also allowing DD/MM/CCYY. */
        if (sscanf(dateobs, "%2d/%2d/%4d", &day, &month, &year) < 3) {
          return 5;
        }

      } else if (dateobs[2] == '-' && dateobs[5] == '-') {
        /* Also recognize DD-MM-YY and DD-MM-CCYY */
        if (sscanf(dateobs, "%2d-%2d-%4d", &day, &month, &year) < 3) {
          return 5;
        }
d248 29
a276 3
        /* Not a valid date format. */
        return 5;
      }
d278 7
a284 19
      if (year < 100) year += 1900;

      /* Doesn't have a time. */
      sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day);
    }

    /* Compute MJD. */
    mjdobs = (double)((1461*(year - (12-month)/10 + 4712))/4
             + (306*((month+9)%12) + 5)/10
             - (3*((year - (12-month)/10 + 4900)/100))/4
             + day - 2399904)
             + (hour + (minute + sec/60.0)/60.0)/24.0;

    if (undefined(wcs->mjdobs)) {
      wcs->mjdobs = mjdobs;
    } else {
      /* Check for consistency. */
      if (fabs(mjdobs - wcs->mjdobs) > 0.5) {
        return 5;
d286 1
a286 2
    }
  }
d288 1
a288 1
  return 0;
d296 1
a296 1
  int  i, status = -1;
d298 1
a298 1
  if (wcs == 0x0) return 1;
d300 3
a302 3
  for (i = 0; i < wcs->naxis; i++) {
    if (wcsutrn(ctrl, wcs->cunit[i]) == 0) status = 0;
  }
d304 1
a304 1
  return status;
d312 57
a368 39
  int k, status;
  struct celprm *wcscel = &(wcs->cel);
  struct prjprm *wcsprj = &(wcscel->prj);

  /* Initialize if required. */
  if (wcs == 0x0) return 1;
  if (wcs->flag != WCSSET) {
    if ((status = wcsset(wcs))) return status;
  }

  /* Was an NCP or GLS projection code translated? */
  if (wcs->lat >= 0) {
    /* Check ctype. */
    if (strcmp(wcs->ctype[wcs->lat]+5, "NCP") == 0) {
      strcpy(wcs->ctype[wcs->lng]+5, "SIN");
      strcpy(wcs->ctype[wcs->lat]+5, "SIN");

      if (wcs->npvmax < wcs->npv + 2) {
        /* Allocate space for two more PVi_ja keyvalues. */
        if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) {
          if (!(wcs->pv = calloc(wcs->npv+2, sizeof(struct pvcard)))) {
            wcs->pv = wcs->m_pv;
            return 2;
          }

          wcs->npvmax = wcs->npv + 2;
          wcs->m_flag = WCSSET;

          for (k = 0; k < wcs->npv; k++) {
            wcs->pv[k] = wcs->m_pv[k];
          }

          if (wcs->m_pv) free(wcs->m_pv);
          wcs->m_pv = wcs->pv;

        } else {
          return 2;
        }

d370 1
d372 1
a372 20
      wcs->pv[wcs->npv].i = wcs->lat + 1;
      wcs->pv[wcs->npv].m = 1;
      wcs->pv[wcs->npv].value = wcsprj->pv[1];
      (wcs->npv)++;

      wcs->pv[wcs->npv].i = wcs->lat + 1;
      wcs->pv[wcs->npv].m = 2;
      wcs->pv[wcs->npv].value = wcsprj->pv[2];
      (wcs->npv)++;

      return 0;

    } else if (strcmp(wcs->ctype[wcs->lat]+5, "GLS") == 0) {
      strcpy(wcs->ctype[wcs->lng]+5, "SFL");
      strcpy(wcs->ctype[wcs->lat]+5, "SFL");
      return 0;
    }
  }

  return -1;
d380 2
a381 16
  char *scode;
  int  i, status;

  /* Initialize if required. */
  if (wcs == 0x0) return 1;
  if (wcs->flag != WCSSET) {
    if ((status = wcsset(wcs))) return status;
  }

  if ((i = wcs->spec) < 0) {
    /* Look for a linear spectral axis. */
    for (i = 0; i < wcs->naxis; i++) {
      if (wcs->types[i]/100 == 30) {
        break;
      }
    }
d383 30
a412 2
    if (i >= wcs->naxis) {
      /* No spectral axis. */
d414 1
a414 2
    }
  }
d416 1
a416 2
  /* Was an AIPS-convention spectral type translated? */
  scode = wcs->ctype[i] + 4;
d418 3
a420 15
  if (strcmp(scode, "-LSR") == 0) {
    strcpy(wcs->specsys, "LSRK");
  } else if (strcmp(scode, "-HEL") == 0) {
    strcpy(wcs->specsys, "BARYCENT");
  } else if (strcmp(scode, "-OBS") == 0) {
    strcpy(wcs->specsys, "TOPOCENT");
  } else {
    return -1;
  }

  strcpy(scode, "\0\0\0\0");

  if (strcmp(wcs->ctype[i], "FELO") == 0) {
    strcpy(wcs->ctype[i], "VOPT-F2W");
  }
d422 1
a422 1
  return 0;
d430 91
a520 91
  unsigned short icnr, indx[NMAX], ncnr;
  int    j, k, stat[4], status;
  double img[4][NMAX], lat, lng, phi[4], phi0, phimax, phimin, pix[4][NMAX],
         *pixj, theta[4], theta0, world[4][NMAX], x, y;

  /* Initialize if required. */
  if (wcs == 0x0) return 1;
  if (wcs->flag != WCSSET) {
    if ((status = wcsset(wcs))) return status;
  }

  /* Check that we have a cylindrical projection. */
  if (wcs->cel.prj.category != CYLINDRICAL) return -1;
  if (wcs->naxis < 2) return -1;


  /* Compute the native longitude in each corner of the image. */
  ncnr = 1 << wcs->naxis;

  for (k = 0; k < NMAX; k++) {
    indx[k] = 1 << k;
  }

  status = 0;
  phimin =  1.0e99;
  phimax = -1.0e99;
  for (icnr = 0; icnr < ncnr;) {
    /* Do four corners at a time. */
    for (j = 0; j < 4; j++, icnr++) {
      pixj = pix[j];

      for (k = 0; k < wcs->naxis; k++) {
        if (icnr & indx[k]) {
          *(pixj++) = naxis[k] + 0.5;
        } else {
          *(pixj++) = 0.5;
        }
      }
    }

    if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0],
                          stat))) {
      for (j = 0; j < 4; j++) {
        if (phi[j] < phimin) phimin = phi[j];
        if (phi[j] > phimax) phimax = phi[j];
      }
    }
  }

  if (phimin > phimax) return status;

  /* Any changes needed? */
  if (phimin >= -180.0 && phimax <= 180.0) return -1;


  /* Compute the new reference pixel coordinates. */
  phi0 = (phimin + phimax) / 2.0;
  theta0 = 0.0;

  if ((status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y,
                       stat))) {
    return (status == 2) ? 5 : 9;
  }

  for (k = 0; k < wcs->naxis; k++) {
    img[0][k] = 0.0;
  }
  img[0][wcs->lng] = x;
  img[0][wcs->lat] = y;

  if ((status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0]))) {
    return status;
  }


  /* Compute celestial coordinates at the new reference pixel. */
  if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0],
                       stat))) {
    return status == 8 ? 10 : status;
  }

  /* Compute native coordinates of the celestial pole. */
  lng =  0.0;
  lat = 90.0;
  (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng, &lat, phi, theta);

  wcs->crpix[wcs->lng] = pix[0][wcs->lng];
  wcs->crpix[wcs->lat] = pix[0][wcs->lat];
  wcs->crval[wcs->lng] = world[0][wcs->lng];
  wcs->crval[wcs->lat] = world[0][wcs->lat];
  wcs->lonpole = phi[0] - phi0;
d522 1
a522 1
  return wcsset(wcs);
@


4.3.1.3
log
@Fixed the translation of GLS to SFL.  In the AIPS convention, setting the
reference longitude and latitude simply translates the reference point
(i.e. the map as a whole) to those coordinates without creating an oblique
grid.  This might be effected by adjusting CRPIXja but that is complicated
by the linear transformation and instead is accomplished here by setting
(phi_0,theta_0).
@
text
@d31 1
a31 1
  $Id: wcsfix.c,v 4.3.1.2 2009/07/22 04:28:39 cal103 Exp cal103 $
d350 1
a367 47

      if (wcs->crval[wcs->lng] != 0.0 || wcs->crval[wcs->lat] != 0.0) {
        /* In the AIPS convention, setting the reference longitude and
         * latitude for GLS simply translates the reference point (i.e. the
         * map as a whole) to those coordinates without creating an oblique
         * grid.  This might be effected by adjusting CRPIXja but that is
         * complicated by the linear transformation and instead is
         * accomplished here by setting (phi_0,theta_0). */
        if (wcs->npvmax < wcs->npv + 2) {
          /* Allocate space for three more PVi_ja keyvalues. */
          if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) {
            if (!(wcs->pv = calloc(wcs->npv+3, sizeof(struct pvcard)))) {
              wcs->pv = wcs->m_pv;
              return 2;
            }

            wcs->npvmax = wcs->npv + 3;
            wcs->m_flag = WCSSET;

            for (k = 0; k < wcs->npv; k++) {
              wcs->pv[k] = wcs->m_pv[k];
            }

            if (wcs->m_pv) free(wcs->m_pv);
            wcs->m_pv = wcs->pv;

          } else {
            return 2;
          }
        }

        wcs->pv[wcs->npv].i = wcs->lng + 1;
        wcs->pv[wcs->npv].m = 0;
        wcs->pv[wcs->npv].value = 1.0;
        (wcs->npv)++;

        wcs->pv[wcs->npv].i = wcs->lng + 1;
        wcs->pv[wcs->npv].m = 1;
        wcs->pv[wcs->npv].value = wcs->crval[wcs->lng];
        (wcs->npv)++;

        wcs->pv[wcs->npv].i = wcs->lng + 1;
        wcs->pv[wcs->npv].m = 2;
        wcs->pv[wcs->npv].value = wcs->crval[wcs->lat];
        (wcs->npv)++;
      }

@


4.2
log
@WCSLIB 4.2: added cdfix().
@
text
@d2 30
a31 29
*
*   WCSLIB 4.2 - an implementation of the FITS WCS standard.
*   Copyright (C) 1995-2005, Mark Calabretta
*
*   WCSLIB 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 2 of the License, or (at your option)
*   any later version.
*
*   WCSLIB 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 WCSLIB; if not, write to the Free Software Foundation, Inc.,
*   59 Temple Place, Suite 330, Boston, MA  02111-1307, USA
*
*   Correspondence concerning WCSLIB may be directed to:
*      Internet email: mcalabre@@atnf.csiro.au
*      Postal address: Dr. Mark Calabretta
*                      Australia Telescope National Facility, CSIRO
*                      PO Box 76
*                      Epping NSW 1710
*                      AUSTRALIA
*
*   Author: Mark Calabretta, Australia Telescope National Facility
*   http://www.atnf.csiro.au/~mcalabre/index.html
*   $Id: wcsfix.c,v 4.1 2005/08/25 13:10:40 cal103 Exp cal103 $
d112 1
a112 1
   if (wcs->altlin & 1 || wcs->altlin & 2 == 0) {
d319 1
a319 1
      if (status = wcsset(wcs)) return status;
d330 1
a330 1
            /* Allocate space for two more PVi_ja cards. */
d386 1
a386 1
      if (status = wcsset(wcs)) return status;
d438 1
a438 1
      if (status = wcsset(wcs)) return status;
d489 2
a490 2
   if (status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y,
                       stat)) {
d500 1
a500 1
   if (status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0])) {
d506 2
a507 2
   if (status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0],
                       stat)) {
@


4.2.1.1
log
@WCSLIB 4.2 development branch.
@
text
@d3 1
a3 1
*   WCSLIB 4.3 - an implementation of the FITS WCS standard.
d30 1
a30 1
*   $Id: wcsfix.c,v 4.2 2005/09/22 09:14:18 cal103 Exp cal103 $
@


4.2.1.2
log
@Parenthesised a boolean expression that was otherwise incorrect and also
parenthesised assignments used as truth values.
@
text
@d4 1
a4 1
*   Copyright (C) 1995-2007, Mark Calabretta
d30 1
a30 1
*   $Id: wcsfix.c,v 4.2.1.1 2005/12/05 02:39:43 cal103 Exp cal103 $
d111 1
a111 1
   if ((wcs->altlin & 1) || !(wcs->altlin & 2)) {
d318 1
a318 1
      if ((status = wcsset(wcs))) return status;
d385 1
a385 1
      if ((status = wcsset(wcs))) return status;
d437 1
a437 1
      if ((status = wcsset(wcs))) return status;
d488 2
a489 2
   if ((status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y,
                        stat))) {
d499 1
a499 1
   if ((status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0]))) {
d505 2
a506 2
   if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0],
                        stat))) {
@


4.2.1.3
log
@Replace use of the obsolete term "card".
@
text
@d30 1
a30 1
*   $Id: wcsfix.c,v 4.2.1.2 2007/06/29 00:58:20 cal103 Exp cal103 $
d329 1
a329 1
            /* Allocate space for two more PVi_ja keyvalues. */
@


4.2.1.4
log
@Switched to LGPL 3.0.
@
text
@d2 29
a30 30

    WCSLIB 4.3 - an implementation of the FITS WCS standard.
    Copyright (C) 1995-2007, Mark Calabretta

    This file is part of WCSLIB.

    WCSLIB is free software: you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License as published by the
    Free Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    WCSLIB 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 Lesser General Public License for
    more details.

    You should have received a copy of the GNU Lesser General Public License
    along with WCSLIB.  If not, see <http://www.gnu.org/licenses/>.

    Correspondence concerning WCSLIB may be directed to:
       Internet email: mcalabre@@atnf.csiro.au
       Postal address: Dr. Mark Calabretta
                       Australia Telescope National Facility, CSIRO
                       PO Box 76
                       Epping NSW 1710
                       AUSTRALIA

    Author: Mark Calabretta, Australia Telescope National Facility
    http://www.atnf.csiro.au/~mcalabre/index.html
    $Id: wcsfix.c,v 4.2.1.3 2007/08/02 04:27:06 cal103 Exp cal103 $
@


4.1
log
@WCSLIB 4.1: generalization of cylfix.c containing implementations of new
functions wcsfix(), datfix(), unitfix(), celfix() and spcfix(); set
wcsfix_errmsg[0] to "success".
@
text
@d3 1
a3 1
*   WCSLIB 4.1 - an implementation of the FITS WCS standard.
d30 1
a30 1
*   $Id$
d70 4
d103 40
d311 1
a311 1
   int elsize, k, status;
d331 1
a331 2
               elsize = sizeof(struct pvcard);
               if (!(wcs->pv = calloc(wcs->npv+2, elsize))) {
@


4.0
log
@WCSLIB 4.0: changed from LGPL to GPL.
@
text
@d3 1
a3 1
*   WCSLIB 4.0 - an implementation of the FITS WCS standard.
d30 1
a30 1
*   $Id: cylfix.c,v 3.6 2004/08/25 04:10:18 cal103 Exp cal103 $
d33 3
d37 3
d41 4
a44 1
#include "sph.h"
a48 2
#define WCSSET 137

d50 2
a51 2
const char *cylfix_errmsg[] = {
   0,
d63 319
a381 1
int cylfix(naxis, wcs)
d383 1
a383 2
const int naxis[];
struct wcsprm *wcs;
d392 1
a392 1
   if (wcs == 0) return 1;
@


3.6
log
@WCSLIB 3.6: "error status/code/number" is now referred to consistently as the
"status return value"; replaced "error" with "failed" in status messages.
@
text
@d3 2
a4 2
*   WCSLIB 3.6 - an implementation of the FITS WCS convention.
*   Copyright (C) 1995-2004, Mark Calabretta
d6 4
a9 4
*   This library is free software; you can redistribute it and/or modify it
*   under the terms of the GNU Library General Public License as published
*   by the Free Software Foundation; either version 2 of the License, or (at
*   your option) any later version.
d11 4
a14 4
*   This library 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 Library
*   General Public License for more details.
d16 3
a18 3
*   You should have received a copy of the GNU Library General Public License
*   along with this library; if not, write to the Free Software Foundation,
*   Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
d30 1
a30 1
*   $Id: cylfix.c,v 3.5 2004/06/28 01:45:40 mcalabre Exp mcalabre $
@


3.5
log
@WCSLIB 3.5
@
text
@d3 1
a3 1
*   WCSLIB 3.5 - an implementation of the FITS WCS convention.
d30 1
a30 1
*   $Id: cylfix.c,v 3.4 2004/02/11 00:15:03 mcalabre Exp mcalabre $
d42 1
a42 1
/* Map error number to error message. */
d46 1
a46 1
   "Memory allocation error",
@


3.4
log
@WCSLIB 3.4
@
text
@d3 1
a3 1
*   WCSLIB 3.4 - an implementation of the FITS WCS convention.
d29 2
a30 1
*   $Id: cylfix.c,v 3.3 2003/10/26 23:03:59 mcalabre Exp mcalabre $
d46 1
a46 1
   "Memory allocation error", 
@


3.3
log
@Change RCS revision to 3.3.
@
text
@d3 2
a4 2
*   WCSLIB 3.3 - an implementation of the FITS WCS convention.
*   Copyright (C) 1995-2003, Mark Calabretta
d29 1
a29 1
*   $Id: cylfix.c,v 1.5 2003/10/21 08:52:49 mcalabre Exp mcalabre $
@


1.5
log
@WCSLIB 3.3
@
text
@d29 1
a29 1
*   $Id: cylfix.c,v 1.4 2003/09/09 04:04:04 mcalabre Exp mcalabre $
@


1.4
log
@WCSLIB 3.2: rationalized function return codes and messages.
@
text
@d3 1
a3 1
*   WCSLIB 3.2 - an implementation of the FITS WCS convention.
d29 1
a29 1
*   $Id: cylfix.c,v 1.3 2003/04/28 03:50:41 mcalabre Exp mcalabre $
@


1.3
log
@WCSLIB 3.1
Moved from the pgsbox directory; prologue comments moved to cylfix.h; code
adapted to the new WCSLIB interfaces; removed FORTRAN wrapper.
@
text
@d3 1
a3 1
*   WCSLIB 3.1 - an implementation of the FITS WCS convention.
d29 1
a29 1
*   $Id: cylfix.c,v 1.2 2002/11/06 06:47:52 mcalabre Exp mcalabre $
d41 14
d62 1
a62 1
   int    err, j, k, stat[4];
d67 1
d69 1
a69 1
      if (err = wcsset(wcs)) return err;
d84 1
a84 1
   err = 0;
d101 2
a102 2
      if (!(err = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0],
                         stat))) {
d110 1
a110 1
   if (phimin > phimax) return err;
d120 3
a122 3
   if (err = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y,
                    stat)) {
      return err == 1 ? 4 : 8;
d131 2
a132 2
   if (err = linx2p(&(wcs->lin), 1, 0, img[0], pix[0])) {
      return err;
d137 3
a139 2
   if (err = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0], stat)) {
      return err == 7 ? 9 : err;
@


1.2
log
@Two bug fixes: use prjfwd() and linfwd() to compute the new reference pixel
instead of wcsfwd() because celfwd() normalizes the native longitude; also,
account for swapped celestial axes.
@
text
@d3 2
a4 2
*   PGSBOX - a non-linear coordinate axis plotter for PGPLOT.
*   Copyright (C) 1997-2002, Mark Calabretta
d6 13
a18 4
*   This library is free software; you can redistribute it and/or
*   modify it under the terms of the GNU Library General Public
*   License as published by the Free Software Foundation; either
*   version 2 of the License, or (at your option) any later version.
d20 1
a20 10
*   This library 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
*   Library General Public License for more details.
*
*   You should have received a copy of the GNU Library General Public
*   License along with this library; if not, write to the Free
*   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*   Correspondence concerning PGSBOX may be directed to:
d22 4
a25 4
*      Postal address: Dr. Mark Calabretta,
*                      Australia Telescope National Facility,
*                      P.O. Box 76,
*                      Epping, NSW, 2121,
a27 46
*=============================================================================
*
*   cylfix() fixes WCS FITS header cards for malformed cylindrical projections
*   that suffer from the problem described in Sect. 7.3.4 of "Representations
*   of Celestial Coordinates in FITS" by Calabretta & Greisen, 2002.
*
*   Given:
*      naxis[2] const int
*                        Image axis lengths.
*
*   Given and returned:
*      crpix[2] double   Coordinate reference pixels.
*
*   Given:
*      pc[2][2] const double
*                        Linear transformation matrix elements.
*      cdelt[2] const double
*                        Axis scales.
*      ctype[2][9]
*               const char
*                        Coordinate type.
*      projp[10]
*               const double
*                        Projection parameters.
*
*   Given and returned:
*      crval[2] double   Coordinate reference values, deg.
*      lonpol   double   Native longitude of the celestial pole.
*
*   Given:
*      latpol   const double
*                        Native latitude  of the celestial pole.
*
*   Function return value:
*               int      Error status
*                          -1: No change required (not an error).
*                           0: Success.
*                           1: Celestial transformation error.
*
*
*   Notes
*   -----
*    1) A FORTRAN wrapper, cylfix_(), is supplied with this C routine.  It
*       should be called with a CHARACTER CTYPE(2)*8 variable for the ctype
*       argument.
*
d29 1
a29 1
*   $Id: cylfix.c,v 1.1 2002/11/06 00:39:55 mcalabre Exp $
d33 5
a37 2
#include <sph.h>
#include <wcs.h>
d39 1
a39 1
int cylfix(naxis, crpix, pc, cdelt, ctype, projp, crval, lonpol, latpol)
d41 1
a41 9
const int    naxis[2];
double       crpix[2];
const double pc[2][2];
const double cdelt[2];
const char   ctype[][9];
const double projp[10];
double       crval[2];
double       *lonpol;
const double latpol;
d43 3
d47 9
a55 9
   int    ix, iy, j;
   double img[2], phi, phi0, phimax, phimin, pix[2], theta, world[2], x[2],
          y[2];
   char   pcode[4];
   struct linprm lin;
   struct prjprm prj;
   struct celprm cel;
   struct wcsprm wcs;
   static char cyls[] = "-CYP-CEA-CAR-MER";
d58 2
a59 6
   strncpy(pcode, &ctype[0][4], 4);
   if (strncmp(pcode, &ctype[1][4], 4)) return -1;
   if (strncmp(pcode, cyls,    4) &&
       strncmp(pcode, cyls+4,  4) &&
       strncmp(pcode, cyls+8,  4) &&
       strncmp(pcode, cyls+12, 4)) return -1;
d63 5
a67 21
   x[0] = 0.5;
   x[1] = naxis[0] + 0.5;
   y[0] = 0.5;
   y[1] = naxis[1] + 0.5;

   wcs.flag  = 0;
   lin.flag  = 0;
   lin.naxis = 2;
   lin.crpix = crpix;
   lin.pc    = (double *)pc;
   lin.cdelt = (double *)cdelt;

   for (j = 0; j < 10; prj.p[j] = projp[j], j++);
   prj.r0 = 0.0;
   prj.flag = 0;

   cel.ref[0] =   0.0;
   cel.ref[1] =   0.0;
   cel.ref[2] = 999.0;
   cel.ref[3] = 999.0;
   cel.flag   = 0;
d69 1
d72 19
a90 8
   for (iy = 0; iy < 2; iy++) {
      for (ix = 0; ix < 2; ix++) {
         pix[0] = x[ix];
         pix[1] = y[iy];
         if (wcsrev(ctype, &wcs, pix, &lin, img, &prj, &phi, &theta, cel.ref,
                    &cel, world) == 0) {
            if (phi < phimin) phimin = phi;
            if (phi > phimax) phimax = phi;
d95 1
a95 1
   if (phimin > phimax) return 1;
d103 1
d105 3
a107 2
   if (prjfwd(phi0, 0.0, &prj, x, y)) {
      return 1;
d110 2
a111 4
   img[wcs.lng] = x[0];
   img[wcs.lat] = y[0];
   if (linfwd(img, &lin, pix)) {
      return 1;
d113 2
d116 4
d122 2
a123 8
   cel.ref[0] = crval[wcs.lng];
   cel.ref[1] = crval[wcs.lat];
   cel.ref[2] = *lonpol;
   cel.ref[3] = latpol;
   cel.flag   = 0;
   if (wcsrev(ctype, &wcs, pix, &lin, img, &prj, &phi, &theta, cel.ref, &cel,
              world)) {
      return 1;
d127 9
a135 40
   if (sphfwd(0.0, 90.0, cel.euler, &phi, &theta)) return 1;

   crpix[0] = pix[0];
   crpix[1] = pix[1];
   crval[0] = world[0];
   crval[1] = world[1];
   *lonpol  = phi - phi0;

   return 0;
}

/*--------------------------------------------------------------------------*/

int cylfix_(naxis, crpix, pc, cdelt, ctype, projp, crval, lonpol, latpol)

const int    naxis[2];
double       crpix[2];
const double pc[2][2];
const double cdelt[2];
const char   ctype[][8];
const double projp[10];
double       crval[2];
double       *lonpol;
const double *latpol;

{
   char ctypes[2][9];
   double pcs[2][2];

   /* Null-terminate the strings. */
   strncpy(&ctypes[0][0], &ctype[0][0], 8);
   strncpy(&ctypes[1][0], &ctype[1][0], 8);
   ctypes[0][8] = '\0';
   ctypes[1][8] = '\0';

   /* Transpose column-major to row-major order. */
   pcs[0][0] = pc[0][0];
   pcs[0][1] = pc[1][0];
   pcs[1][0] = pc[0][1];
   pcs[1][1] = pc[1][1];
d137 1
a137 2
   return cylfix(naxis, crpix, pcs, cdelt, ctypes, projp, crval, lonpol,
                 *latpol);
@


1.1
log
@Initial revision
@
text
@d75 1
a75 1
*   $Id$
d159 8
a166 4
   world[0] = phi0;
   world[1] = 0.0;
   if (wcsfwd(ctype, &wcs, world, cel.ref, &cel, &phi, &theta, &prj, img,
              &lin, pix)) {
d170 1
d172 2
a173 2
   cel.ref[0] = crval[0];
   cel.ref[1] = crval[1];
@
