                                                                                

#!/usr/bin/perl -w
#      collect_stars.pl    12 March 2002  Tom Droege
#      rev                 20 March 2002  Now ver 1.0   
# Given a photom .cal file, this program brings all the observations
# of the same physical star together in one place.
#   
# This is a modification of Michael Richmond's calc_gammas_cal.pl
# which did other things to the data.  This version just collects
# all the observations of the same star together from a .cal file.  
# The user must SORT INPUT FILE BY RA in order for this program
# to work properly.
#
# It prints out an ASCII text file with one line per observation,
# grouping the lines for the same star together. The leading 
# number of each line indicates is a star sequence number.
# The second number indicates how many stars are in the group.
#
# usage:    sort -n +1 M*.cal | collect_stars.pl  
#
#   where
#               M*.cal   is a calibrated photometry file created
#                        by the pipeline
#               
# Input format:
#
# star_id ra dec jd "V" vmag vsig vflag "I" imag isig iflag           
#                        
#
# Output format:
# 
# seq_num num_obs avg_ra avg_dec jd_array[] 
# "V" vmag_array[] vsig_array avg_vsig vflag
# "I" imag_array[] isig_array avg_isig iflag
# 
# we need to add an overflow check on num_obs
#use strict;

$debug = 0;

# change these items to add more lists or list length
$number_of_lists = 40; #$ARGV[0];
$list_spacing = 200;
for ($i = 0; $i < $number_of_lists; $i++) {
$num_obs[$i] = 0;
$list_has_been_used[$i] = 0;  
#list_has_been_used is necessary to avoid a problem
#for a star at 0,0 i.e. we cannot test for a 0,0 entry 
#and be sure it is not a real star.
}

# other variables
$active_list_index = 0;
$next_list_to_unload = 0;
$iicl = 0;  # index into current list 
$num_obs = 0;
$line_count = 0;
$match_radius =  40;  $ARGV[0];    # arcseconds  ###
$no_empty_lists = 0;
$match = 0;
$seq_num = 1;

#temp values to make system run

$cur_ra = 9999.9;
$cur_dec = 9999.9;
$num_obs = 0;

# we accumulate values for statistical purposes in these arrays

$star_id[0] = 0;
$cur_ra[0] = 9999.0;    # star RA degrees
$cur_dec[0] = 9999.0;  # star dec degrees
$jd_array[0] = 9999.0; # Julian date of measurement
$vmag_array[0] = 0;     # V filter magnitude
$vsig_array[0] = 0;       # V filter measurement sigma
$vflag_array[0] = 0;      # V measurement error flags
$imag_array[0] = 0;      # I filter magnitude
$isig_array[0] = 0;        # I fliter measurement sigma  
$iflag_array[0] = 0;       # I measurement error flags

#####################################################
#                                                   # 
# Here we start the main loop to get a star         #
# from the list and try to match it with one of     #
#  number_of_lists in memory                        #
#                                                   #
#####################################################


while (<STDIN>) {
    $line_count++;
    $line = $_;
    @words = split(/\s+/, $line);
    $star_id = $words[1];
    $ra = $words[2];
    $dec = $words[3];
    $jd = $words[4];
    $vmag = $words[6];
    $vsig = $words[7];
    $vflag = $words[8];
    $imag = $words[10];
    $isig = $words[11];
    $iflag = $words[12];

    if (0 == 1) {
        printf "%9.4f %9.4f %13.5f  %6.3f %6.3f  %6.3f %6.3f \n", 
        $ra, $dec, $jd, $vmag, $vsig, $imag, $isig;
    }

    # ignore stars if flags indicate possible problems
    if (0 == 1) {
        if (($vflag != 0) || ($iflag != 0)) {
          next;
        }
    }

##################################################
#                                                # 
#         First time through initialization      #
#         Put the first star on the first list   #
#                                                # 
##################################################

    if ($line_count eq 1) {
        # do it 
        $star_id[0] = $star_id;
        $cur_ra[0] = $ra;
        $cur_dec[0] = $dec;      # degrees
        $jd_array[0] = $jd;
        $vmag_array[0] = $vmag;
        $vsig_array[0] = $vsig;
        $vflag_array[0] = $vflag;
        $imag_array[0] = $imag;
        $isig_array[0] = $isig;
        $iflag_array[0] = $iflag;
        $next_list_to_unload = 0; 
        $num_obs[0] = 1;
        $list_has_been_used[0] = 1;
        next;
    }
###################################################
#                                                 #
#  First, look to see if the current star matches #
#  any list.                                      #
#                                                 #
###################################################
    $match = 0;
  
    for ($active_list_index = 0; $active_list_index < 
        $number_of_lists;            
        $active_list_index++) {
        
        if (coords_match ($ra, $dec, 
        $cur_ra[$active_list_index * $list_spacing], 
        $cur_dec[$active_list_index * $list_spacing],
        $match_radius) == 1) {
           $match = 1;
         
        }

        if ($match == 1) {
            # figure out index into the right list
           
            
          
            $iicl = ($active_list_index * $list_spacing) +                 
            $num_obs[$active_list_index];
      
            # put line on an existing list
            # do it here
            $star_id[$iicl] = $star_id;
            $cur_ra[$iicl] = $ra;
            $cur_dec[$iicl] = $dec;      # degrees
            $jd_array[$iicl] = $jd;
            $vmag_array[$iicl] = $vmag;
            $vsig_array[$iicl] = $vsig;
            $vflag_array[$iicl] = $vflag;
            $imag_array[$iicl] = $imag;
            $isig_array[$iicl] = $isig;
            $iflag_array[$iicl] = $iflag;
            $num_obs[$active_list_index]++;# index after loading
            last;
            #stop the loop.  We have found a match
            #and put the item on a list.  Later we
            #we will write code which catches a match
            #on more than one list       
        }
     }

###################################################
#                                                 #
#  Second, check if there are empty lists         #
#  available which can happen at the start of     #
#  the list filling process                       #
#                                                 #
###################################################                        
                       
                                      
    if (($match == 0) && ($no_empty_lists == 0)) {
    # check if there are empty lists available which can happen at
    #the start of the loading process
        $empty_list_to_use = 0;
        for ($i = 0; $i < $number_of_lists; $i++) {
            if ($list_has_been_used[$i] == 0) {
            $empty_list_to_use = $i;
            $list_has_been_used[$i] = 1;
            last;
            }
        }
        if ($empty_list_to_use == 0) {
        $no_empty_lists = 1;
        } 
       
        if ($empty_list_to_use > 0) {
            # there are empty lists put on next available
            # compute the index to us to first item on list
            $iicl = $empty_list_to_use * $list_spacing;
            # here put on list
            $star_id[$iicl] = $star_id;
            $cur_ra[$iicl] = $ra;
            $cur_dec[$iicl] = $dec;      # degrees
            $jd_array[$iicl] = $jd;
            $vmag_array[$iicl] = $vmag;
            $vsig_array[$iicl] = $vsig;
            $vflag_array[$iicl] = $vflag;
            $imag_array[$iicl] = $imag;
            $isig_array[$iicl] = $isig;
            $iflag_array[$iicl] = $iflag;
            $num_obs[$empty_list_to_use] = 1; #indes after loading
         }             
    } 
   
#######################################################
#                                                     #
# Third, if there are no empty lists, unload the      #
# oldest list and load the new line into the          #
# newly emptied list                                  #
#                                                     #
####################################################### 
               
    if (($match == 0) && ($no_empty_lists == 1)) {    
    # use next_list_to_unload
    # compute index into start of list
    
    $iicl = $next_list_to_unload * $list_spacing;
    
        if (1 == 1) {            
        # unload next_list_to_unload
            for ($i = $iicl; $i < ($iicl + 
                $num_obs[$next_list_to_unload]); $i++) {
                $star_id[$i] = $num_obs[$next_list_to_unload];
            }
            for ($i = $iicl; $i < ($iicl + 
                $num_obs[$next_list_to_unload]); $i++) {
printf  "%4d %3d %8.4f %6.4f %13.5f V %6.3f %4.3f %3d I %6.3f %4.3f %3d \n",
                $seq_num,
                $star_id[$i], 
                $cur_ra[$i], 
                $cur_dec[$i], 
                $jd_array[$i],
                $vmag_array[$i],                                           
                $vsig_array[$i], 
                $vflag_array[$i], 
                $imag_array[$i],
                $isig_array[$i], 
                $iflag_array[$i];
            }
            $seq_num++;
        }
    # load next_list_to_unload with new star
    # we are loading position 0
        $star_id[$iicl] = $star_id;
        $cur_ra[$iicl] = $ra;
        $cur_dec[$iicl] = $dec;      # degrees
        $jd_array[$iicl] = $jd;
        $vmag_array[$iicl] = $vmag;
        $vsig_array[$iicl] = $vsig;
        $vflag_array[$iicl] = $vflag;
        $imag_array[$iicl] = $imag;
        $isig_array[$iicl] = $isig;
        $iflag_array[$iicl] = $iflag;
        $num_obs[$next_list_to_unload] = 1;
        $next_list_to_unload++;
        if ($next_list_to_unload >= $number_of_lists) {
            $next_list_to_unload = 0;
        }
    }
}  # go get next star line 

# we are done but the lists are full, so unload all the lists

for ($j = 0; $j < $number_of_lists; $j++) {
    if ($num_obs[$j] < 1) {
       next; 
    }
    $next_list_to_unload = $j;
    # unload next_list_to_unload
    $iicl = $next_list_to_unload * $list_spacing;
    for ($i = $iicl; $i < ($iicl + 
        $num_obs[$next_list_to_unload]); $i++) {
        $star_id[$i] = $num_obs[$next_list_to_unload];
    }
    for ($i = $iicl; $i < ($iicl + 
        $num_obs[$next_list_to_unload]); $i++) {
printf  "%4d %3d %8.6f %6.6f %13.5f V %6.3f %4.3f %3d I %6.3f %4.3f %3d \n",
        $seq_num,
        $star_id[$i], 
        $cur_ra[$i], 
        $cur_dec[$i], 
        $jd_array[$i],
        $vmag_array[$i],                                           
        $vsig_array[$i], 
        $vflag_array[$i], 
        $imag_array[$i],
        $isig_array[$i], 
        $iflag_array[$i];
    }
    $seq_num++;   
}

exit 0;


#####################################################################
# PROCEDURE: coords_match
#
# DESCRIPTION: given two sets of coords (both in decimal degrees),
#              calculate the distance between them.  If the distance
#              is 
#
#                   <= match_radius         then we match: return 1
#                   >  match_radius         then no match: return 0
#
# Note that the "match_radius" argument is in arcseconds, 
#   not degrees.
#
# USAGE:
#         coords_match   this_ra this_dec  old_ra old_dec  
# match_radius
#
sub coords_match {
 
  my($this_ra, $this_dec,
     $old_ra,  $old_dec,
     $match_radius,
     $delta_ra, $delta_dec,
     $avg_dec, $factor,
     $DEGTORAD);
#print "in sub", " ", $_[0], " ", $_[1], " ", $_[2], " ", $_[3], "\n";  ###

  $this_ra = $_[0];
  $this_dec = $_[1];
  $old_ra = $_[2];
  $old_dec = $_[3];
  $match_radius = $_[4];

  # convert matching radius to degrees
  $match_radius /= 3600.0;

  $delta_ra = abs($this_ra - $old_ra);
  $delta_dec = abs($this_dec - $old_dec);

   #printf "match_radius %9.5lf  delta_ra %9.5lf  delta_dec    ###
   #%9.5lf \n",
   #       $match_radius, $delta_ra, $delta_dec;

  # do a quick check first 
  if ($delta_dec > $match_radius) {
    return 0;
  }

  # we need to modify the RA distance due to cos(Dec) factor.
  #   Use the avg dec to calc this factor
  $avg_dec = ($this_dec + $old_dec) / 2.0;
  $DEGTORAD = 0.017453293;
  $factor = cos($avg_dec*$DEGTORAD);
  $delta_ra *= $factor;
  if ($delta_ra > $match_radius) {
    return 0;
  }

  # well, if we get this far, the two coords do match within
  #   a square box of the given matching radius.
  #   Call it a match
  return 1;

}


########################################################################
# PROCEDURE: calc_mean
#
# DESCRIPTION: given the number of elements in an array, and the
#              array itself, calculate the mean value of the array.
#
# RETURNS:
#              the mean value
#
sub calc_mean {

  my($num, @array);
  my($i, $sum);
  my($mean);

  $num = $_[0];
  for ($i = 0; $i < $num; $i++) {
    $array[$i] = $_[$i+1];
  }

  if ($num == 0) {
    $mean = 0.0;
    return($mean);
  }
 
  # printf "%2d ", $num;
  for ($i = 0; $i < $num; $i++) {
  #   printf "%6.3f ", $array[$i];
  }

  $sum = 0.0;
  for ($i = 0; $i < $num; $i++) {
    $sum += $array[$i];
  }
  $mean = $sum/$num;
  # printf(" %7.4f \n", $mean);

  return($mean);
}


########################################################################
# PROCEDURE: calc_sigma
#
# DESCRIPTION: given the number of elements in an array, and the
#              array itself, calculate the standard deviation from the
#              mean of an array
#
# RETURNS:
#              the stdev
#
sub calc_sigma {

  my($num, @array);
  my($i, $sum);
  my($sumsq);
  my($mean, $stdev);

  $num = $_[0];
  for ($i = 0; $i < $num; $i++) {
    $array[$i] = $_[$i+1];
  }

  if ($num < 2) {
    $stdev = 0.0;
    return($stdev);
  }
 
  # printf "%2d ", $num;
  for ($i = 0; $i < $num; $i++) {
  #   printf "%6.3f ", $array[$i];
  }

  $sum = 0.0;
  $sumsq = 0.0;
  for ($i = 0; $i < $num; $i++) {
    $sum += $array[$i];
    $sumsq += $array[$i]*$array[$i];
  }
  $mean = $sum/$num;
  $stdev = sqrt(($sumsq - $num*$mean*$mean)/($num - 1));
  # printf(" %7.4f \n", $stdev);

  return($stdev);
}












