1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
|
/* details.cpp : code to store/access 'observational details' (header)
data for 80-column MPC-formatted astrometry, as documented at
http://www.minorplanetcenter.net/iau/info/ObsDetails.html */
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "stackall.h"
#include "details.h"
typedef struct
{
char **lines;
size_t n_lines;
bool observations_found;
} mpc_code_details_t;
typedef struct
{
void *stack;
mpc_code_details_t *code_details;
int n_code_details;
int curr_idx; /* index into code_details for last COD line seen */
} observation_details_t;
#define min_n_code_details 16
#define min_code_lines 16
void *init_observation_details( void)
{
void *stack = create_stack( 2000);
observation_details_t *rval = (observation_details_t *)
stack_calloc( stack, sizeof( observation_details_t));
rval->stack = stack;
rval->curr_idx = -1; /* i.e., "no COD line yet" */
rval->code_details = (mpc_code_details_t *)stack_calloc( stack,
min_n_code_details * sizeof( mpc_code_details_t));
return( rval);
}
/* A useful piece of bit-twiddling. X & (X-1) clears the lowest */
/* bit of X. If X is a power of two, you've only got one bit */
/* to clear, and the result is zero. */
#define is_power_of_two( X) (!((X) & ((X) - 1)))
/* Binary-search to find the desired MPC code in the 'code_details' array.
Or where that code ought to be put in, if we're adding a new code (in
which case *cmp will be nonzero.) */
static int code_cmp( const char *a, const char *b)
{
int rval;
while( *a == *b && *a)
{
a++;
b++;
}
if( *a <= ' ' && *b <= ' ')
rval = 0;
else
rval = *a - *b;
return( rval);
}
static int find_code_details( const observation_details_t *det,
const char *mpc_code, int *cmp)
{
int loc = -1, stepsize = (int)0x8000, compare = 1, loc1;
while( compare && (stepsize >>= 1))
if( (loc1 = loc + stepsize) < det->n_code_details)
{
assert( det->code_details[loc1].lines);
compare = code_cmp( mpc_code, det->code_details[loc1].lines[0] + 4);
if( compare >= 0)
loc = loc1;
}
*cmp = compare;
return( loc);
}
const char **get_code_details( const void *obs_details, const char *mpc_code)
{
const observation_details_t *det = (const observation_details_t *)obs_details;
int idx, compare;
#ifdef DEBUG_PRINTFS
printf( "We've got %d details :", det->n_code_details);
for( idx = 0; idx < det->n_code_details; idx++)
printf( " '%s'", det->code_details[idx].lines[0]);
printf( " And that's it\n");
#endif
idx = find_code_details( det, mpc_code, &compare);
if( !compare)
return( (const char **)det->code_details[idx].lines);
else
return( NULL);
}
static int reset_mpc_code( observation_details_t *det, const char *mpc_code)
{
int compare, idx = find_code_details( det, mpc_code, &compare);
#ifdef DEBUG_PRINTFS
printf( "Resetting MPC code: idx %d, compare %d, %d details\n",
idx, compare, det->n_code_details);
#endif
if( compare) /* not an MPC code we've already encountered */
{
idx++;
det->n_code_details++;
if( is_power_of_two( det->n_code_details) && det->n_code_details >= min_n_code_details)
{
mpc_code_details_t *new_array = (mpc_code_details_t *)
stack_alloc( det->stack, 2 * det->n_code_details * sizeof( mpc_code_details_t));
memcpy( new_array, det->code_details, det->n_code_details * sizeof( mpc_code_details_t));
det->code_details = new_array;
}
#ifdef DEBUG_PRINTFS
printf( "Moving memory\n");
#endif
memmove( det->code_details + idx + 1, det->code_details + idx,
(det->n_code_details - idx) * sizeof( mpc_code_details_t));
#ifdef DEBUG_PRINTFS
printf( "Moved memory\n");
#endif
}
det->code_details[idx].observations_found = false;
det->code_details[idx].n_lines = 0;
det->code_details[idx].lines = (char **)stack_calloc( det->stack,
(min_code_lines + 1) * sizeof( char *));
det->curr_idx = idx;
#ifdef DEBUG_PRINTFS
printf( "Done\n");
#endif
return( 0);
}
static int probable_mpc_record( const char *buff)
{
int i, rval = 1; /* assume it's a valid record */
if( buff[12] != ' ' && buff[12] != '*' && buff[12] != '-')
rval = 0;
for( i = 77; i < 80; i++)
if( buff[i] <= ' ' || buff[i] > 'z')
rval = 0;
return( rval);
}
int add_line_to_observation_details( void *obs_details, const char *iline)
{
observation_details_t *det = (observation_details_t *)obs_details;
size_t len = strlen( iline);
const char *valid_lines = "COD CON OBS MEA TEL NET BND COM NUM ACK AC2 ";
int i, compare = 1, rval;
while( len && (iline[len - 1] == 10 || iline[len - 1] == 13))
len--; /* drop trailing carriage returns/line feeds */
if( !memcmp( iline, "COD ", 4))
reset_mpc_code( det, iline + 4);
if( det->curr_idx < 0) /* no COD line seen yet */
return( OBS_DETAILS_IRRELEVANT_LINE);
assert( det->curr_idx < det->n_code_details);
if( len == 80 && probable_mpc_record( iline))
{
const int idx = find_code_details( det, iline + 77, &compare);
if( !compare)
{
mpc_code_details_t *mptr = det->code_details + idx;
#ifdef DEBUG_PRINTFS
if( !mptr->observations_found)
printf( "Got an observation for '%s'\n", mptr->lines[0]);
#endif
mptr->observations_found = true;
}
return( OBS_DETAILS_MPC_80_COLUMN_LINE);
}
if( len < 4 || iline[3] != ' ')
return( OBS_DETAILS_IRRELEVANT_LINE);
for( i = 0; compare && valid_lines[i]; i += 4)
compare = memcmp( iline, valid_lines + i, 4);
if( !compare) /* yup, it's a valid header line */
{
mpc_code_details_t *mptr = det->code_details + det->curr_idx;
const bool reallocation_needed = (mptr->observations_found
|| (is_power_of_two( mptr->n_lines)
&& mptr->n_lines >= min_code_lines));
if( reallocation_needed)
{
size_t new_size = min_code_lines;
char **tptr;
while( new_size < mptr->n_lines)
new_size <<= 1;
tptr = (char **)stack_calloc( det->stack,
(2 * new_size + 1) * sizeof( char *));
memcpy( tptr, mptr->lines, mptr->n_lines * sizeof( char *));
mptr->lines = tptr;
}
mptr->lines[mptr->n_lines] = (char *)stack_calloc( det->stack, len + 1);
memcpy( mptr->lines[mptr->n_lines], iline, len);
mptr->n_lines++;
rval = OBS_DETAILS_HEADER_LINE;
}
else
rval = OBS_DETAILS_IRRELEVANT_LINE;
return( rval);
}
void free_observation_details( void *obs_details)
{
observation_details_t *det = (observation_details_t *)obs_details;
destroy_stack( det->stack);
}
/* init_observation_details allocates the stack (see 'stackall.h/cpp'),
allocates its own return value from that stack, sets up an initial
'code_details' array, and nothing else.
Each line read from the file is then fed through get_code_details(). If the
line starts with COD, we look for it in the existing code_details() array
(using get_code_details()) and either find it there or insert a new entry in
the array. (For speed, 'code_details' is sorted so we can binary-search.)
When we see COD, we make sure that 'lines' is allocated to (say) eight
lines, n_lines is zeroed, and 'observations_found' is set to false.
If it looks like an 80-column MPC observation that matches the current
MPC code, we set 'observations_found' for that code to be true.
If it's a CON, OBS, MEA, TEL, etc. line, and 'observations_found' is
false, we can just add the new line to the mpc_code_details_t structure
for that MPC code. (CON/OBS/MEA lines accumulate; the others replace as
we see them.)
If 'observations_found' is true, we have to allocate a new
mpc_code_details_t structure and copy over the old one and change the
'code_details' pointer accordingly, and _then_ add the new line. (After
which, 'observations_found' is reset to false.)
The reason for this is that if we have an observation file such as...
COD XYZ
OBS B. Frank
NET GSC-1.1
(six observations from XYZ)
NET Gaia-DR2
COM Later observations
(three more observations from XYZ)
...we'll set up an mpc_code_details_t structure for XYZ when we see
the COD line, and add the OBS and NET lines. The first six observations
will be associated with that structure.
When the second NET line is read, though, observations_found == true
tells us we need to allocate a new mpc_code_details_t, copy over the
existing text, replace the NET line, and set (in the new struct)
observations_found = false. Then we add in the COM line, and the next
three observations are associated with the new structure.
The result is that (in the above case) we only allocate two 'details'
structures, and most of the lines they point to overlap.
get_code_details() just does a binary search within the code_details
array.
free_observation_details() should be a single line, calling stack_free
for the 'stack' variable. (Everything allocated here ought to be on the
stack-based allocator.) */
|