File Coverage

ABI.pm
Criterion Covered Total %
statement 259 285 90.8
branch 60 72 83.3
condition 2 6 33.3
subroutine 27 28 96.4
pod 11 12 91.6
total 359 403 89.0


line stmt bran cond sub pod time code
1             #$Id: ABI.pm,v 1.3.2.4 2006/11/20 03:18:12 malay Exp $
2             # Perl module to parse ABI chromatogram file
3             # Malay
4             # Copyright (c) 2002, 2003, 2004, 2005, 2006 Malay Kumar Basu
5             # You may distribute this module under the same terms as perl itself
6             # Thanks to David H. Klatte for all the hard work!
7             package ABI;
8 1     1   1928 use IO::File;
  1         11916  
  1         154  
9 1     1   9 use Carp;
  1         3  
  1         58  
10 1     1   17 use strict;
  1         2  
  1         3280  
11              
12             =head1 NAME
13              
14             ABI.pm - Perl module to parse chromatogram files generated by
15             Applied Biosystems (ABI) automated DNA sequencing machine.
16              
17             Please cite:
18             ABI.pm - Perl module to parse chromatogram files generated by
19             Applied Biosystems (ABI) automated DNA sequencing machine.
20             by Malay K Basu (malay@bioinformatics.org); source code available at:
21             http://search.cpan.org/~malay
22              
23             =head1 VERSION
24              
25             Version 1.0
26              
27             =cut
28              
29             our $VERSION = '1.0';
30              
31             =head1 SYNOPSIS
32              
33             my $abi = ABI->new(-file=>"mysequence.abi");
34             my $seq = $abi->get_sequence(); # To get the sequence
35             my @trace_a = $abi->get_trace("A"); # Get the raw traces for "A"
36             my @trace_g = $abi->get_trace("G"); # Get the raw traces for "G"
37             my @base_calls = $abi->get_base_calls(); # Get the base calls
38              
39             =head1 DESCRIPTION
40              
41             An ABI chromatogram file is in binary format. It contain several
42             information only some of which is required for normal use. This
43             module only gives access to the most used information stored in
44             ABI file. All the accesses are read only.
45              
46             If you have edited the file using a trace editor, then you can use the corresponding
47             method to access the edited sequence and base calls.
48              
49              
50              
51             =head1 CONSTRUCTOR
52              
53             =head2 new()
54              
55             Usage : $abi = ABI->new(-file=>"filename");
56             $abi = ABI->new("filename"); # same thing
57              
58             =cut
59              
60             sub new {
61 1     1 1 990 my $class = shift;
62 1         3 my $self = {};
63 1   33     10 bless $self, ref($class) || $class;
64 1         639 $self->_init(@_);
65              
66             #print "****", $self->{_mac_header}, "\n";
67 1         9 return $self;
68             }
69              
70             sub _init {
71 1     1   4 my ( $self, @args ) = @_;
72 1         8 my ($file) = $self->_rearrange( ["FILE"], @args );
73 1 50       4 if ( !defined($file) ) {
74 0         0 croak "Can't open the input file\n";
75             } else {
76 1         3 $self->set_file_handle($file);
77             }
78 1         3 $self->{_sequence} = "";
79 1         2 $self->{_sequence_corrected} = "";
80 1         2 $self->{_sample} = "";
81 1         2 $self->{A} = [];
82 1         2 $self->{T} = [];
83 1         2 $self->{G} = [];
84 1         3 $self->{C} = [];
85 1         1 $self->{_basecalls} = [];
86 1         2 $self->{_basecalls_corrected} = [];
87 1         2 $self->{_trace_length} = 0;
88 1         2 $self->{_seq_length} = 0;
89 1         2 $self->{_seq_length_corrected} = 0;
90 1         1 $self->{_abs_index} = 26;
91 1         2 $self->{_index} = undef;
92 1         2 $self->{PLOC1} = undef;
93 1         2 $self->{PLOC} = undef;
94 1         2 $self->{_a_start} = undef;
95 1         2 $self->{_g_start} = undef;
96 1         2 $self->{_c_start} = undef;
97 1         2 $self->{_t_start} = undef;
98 1         3 $self->{DATA9} = undef;
99 1         2 $self->{DATA10} = undef;
100 1         1 $self->{DATA11} = undef;
101 1         2 $self->{DATA12} = undef;
102 1         1 $self->{PBAS1} = undef;
103 1         2 $self->{PBAS2} = undef;
104 1         1 $self->{FWO} = undef;
105 1         2 $self->{_mac_header} = 0;
106 1         2 $self->{_maximum_trace} = 0;
107              
108 1 50       3 if ( $self->_is_abi() ) {
109              
110             #print "ABI FILE\n";
111 1         4 $self->_set_index();
112 1         4 $self->_set_base_calls();
113 1         15 $self->_set_corrected_base_calls();
114 1         18 $self->_set_seq();
115 1         3 $self->_set_corrected_seq();
116 1         3 $self->_set_traces();
117 1         12 $self->_set_max_trace();
118 1         13 $self->_set_sample_name();
119 1         47 close( $self->{_fh} );
120             }
121 1         6 return $self;
122             }
123              
124             sub set_file_handle {
125 1     1 0 2 my $self = shift;
126 1         1 my $path = shift;
127 1         7 my $fh = IO::File->new();
128 1 50       51 if ( $fh->open("< $path") ) {
129 1         88 binmode($fh);
130 1         8 $self->{_fh} = $fh;
131             } else {
132 0         0 croak "Could not open $path in ABITrace::set_file_handle\n";
133             }
134             }
135              
136             sub _rearrange {
137 1     1   2 my ( $self, $order, @param ) = @_;
138 1 50       5 return unless @param;
139 1 50 33     10 return @param unless ( defined( $param[0] ) && $param[0] =~ /^-/ );
140 0         0 for ( my $i = 0 ; $i < @param ; $i += 2 ) {
141 0         0 $param[$i] =~ s/^\-//;
142 0         0 $param[$i] =~ tr/a-z/A-Z/;
143             }
144              
145             # Now we'll convert the @params variable into an associative array.
146 0         0 local ($^W) = 0; # prevent "odd number of elements" warning with -w.
147 0         0 my (%param) = @param;
148 0         0 my (@return_array);
149              
150             # What we intend to do is loop through the @{$order} variable,
151             # and for each value, we use that as a key into our associative
152             # array, pushing the value at that key onto our return array.
153             my ($key);
154 0         0 foreach $key ( @{$order} ) {
  0         0  
155 0         0 my ($value) = $param{$key};
156 0         0 delete $param{$key};
157 0         0 push( @return_array, $value );
158             }
159              
160             # print "\n_rearrange() after processing:\n";
161             # my $i; for ($i=0;$i<@return_array;$i++) { printf "%20s => %s\n", ${$order}[$i], $return_array[$i]; } ;
162 0         0 return (@return_array);
163             }
164              
165             sub _is_abi {
166 1     1   1 my $self = shift;
167 1         3 my $fh = $self->{"_fh"};
168 1         1 my $buf;
169 1         6 seek( $fh, 0, 0 );
170 1         24 read( $fh, $buf, 3 );
171              
172             #my $a = unpack("n*", $buf);
173 1 50       4 if ( $buf eq "ABI" ) {
174 1         3 return 1;
175             } else {
176 0         0 seek( $fh, 128, 0 );
177 0         0 read( $fh, $buf, 3 );
178 0 0       0 if ( $buf eq "ABI" ) {
179 0         0 $self->_set_mac_header();
180 0         0 return 1;
181             } else {
182 0         0 return 0;
183             }
184             }
185             }
186              
187             sub _set_mac_header {
188 0     0   0 my $self = shift;
189 0         0 $self->{_mac_header} = 128;
190             }
191              
192             sub _set_index {
193 1     1   2 my $self = shift;
194 1         1 my $data_counter = 0;
195 1         2 my $pbas_counter = 0;
196 1         1 my $ploc_counter = 0;
197 1         13 my ( $num_records, $buf );
198              
199             #print $self->{_fh}, "\n";
200             #print $self->{_mac_header}, "\n";
201 1         9 seek( $self->{_fh}, $self->{_abs_index} + $self->{_mac_header}, 0 );
202 1         9 read( $self->{_fh}, $buf, 4 );
203 1         6 $self->{_index} = unpack( "N", $buf );
204              
205             #print $self->{_index};
206 1         9 seek( $self->{_fh}, $self->{_abs_index} - 8 + $self->{_mac_header}, 0 );
207 1         9 read( $self->{_fh}, $buf, 4 );
208 1         3 $num_records = unpack( "N", $buf );
209 1         5 for ( my $i = 0 ; $i <= $num_records - 1 ; $i++ ) {
210 82         562 seek( $self->{_fh}, $self->{_index} + ( $i * 28 ), 0 );
211 82         402 read( $self->{_fh}, $buf, 4 );
212 82 100       136 if ( $buf eq "FWO_" ) {
213 1         6 $self->{FWO} = $self->{_index} + ( $i * 28 ) + 20;
214             }
215 82 100       121 if ( $buf eq "DATA" ) {
216 12         10 $data_counter++;
217 12 100       21 if ( $data_counter == 9 ) {
218 1         4 $self->{DATA9} = $self->{_index} + ( $i * 28 ) + 20;
219             }
220 12 100       16 if ( $data_counter == 10 ) {
221 1         3 $self->{DATA10} = $self->{_index} + ( $i * 28 ) + 20;
222             }
223 12 100       22 if ( $data_counter == 11 ) {
224 1         3 $self->{DATA11} = $self->{_index} + ( $i * 28 ) + 20;
225             }
226 12 100       16 if ( $data_counter == 12 ) {
227 1         3 $self->{DATA12} = $self->{_index} + ( $i * 28 ) + 20;
228             }
229             }
230 82 100       121 if ( $buf eq "PBAS" ) {
231 2         2 $pbas_counter++;
232 2 100       5 if ( $pbas_counter == 1 ) {
233 1         3 $self->{PBAS1} = $self->{_index} + ( $i * 28 ) + 20;
234             }
235 2 100       4 if ( $pbas_counter == 2 ) {
236 1         3 $self->{PBAS2} = $self->{_index} + ( $i * 28 ) + 20;
237             }
238             }
239 82 100       111 if ( $buf eq "PLOC" ) {
240 2         2 $ploc_counter++;
241 2 100       4 if ( $ploc_counter == 1 ) {
242 1         2 $self->{PLOC1} = $self->{_index} + ( $i * 28 ) + 20;
243             }
244 2 100       6 if ( $ploc_counter == 2 ) {
245 1         2 $self->{PLOC} = $self->{_index} + ( $i * 28 ) + 20;
246             }
247             }
248 82 100       201 if ( $buf eq "SMPL" ) {
249 1         3 $self->{SMPL} = $self->{_index} + ( $i * 28 ) + 20;
250             }
251             }
252 1         6 seek( $self->{_fh}, $self->{DATA12} - 8, 0 );
253 1         5 read( $self->{_fh}, $buf, 4 );
254 1         3 $self->{_trace_length} = unpack( "N", $buf );
255 1         8 seek( $self->{_fh}, $self->{PBAS2} - 4, 0 );
256 1         5 read( $self->{_fh}, $buf, 4 );
257 1         2 $self->{_seq_length} = unpack( "N", $buf );
258 1         66 seek( $self->{_fh}, $self->{PBAS1} - 4, 0 );
259 1         7 read( $self->{_fh}, $buf, 4 );
260 1         3 $self->{_seq_length_corrected} = unpack( "N", $buf );
261 1         5 $self->{PLOC} = $self->_get_int( $self->{PLOC} ) + $self->{_mac_header};
262 1         3 $self->{PLOC1} = $self->_get_int( $self->{PLOC1} ) + $self->{_mac_header};
263 1         2 $self->{DATA9} = $self->_get_int( $self->{DATA9} ) + $self->{_mac_header};
264 1         3 $self->{DATA10} = $self->_get_int( $self->{DATA10} ) + $self->{_mac_header};
265 1         3 $self->{DATA11} = $self->_get_int( $self->{DATA11} ) + $self->{_mac_header};
266 1         4 $self->{DATA12} = $self->_get_int( $self->{DATA12} ) + $self->{_mac_header};
267 1         4 $self->{PBAS1} = $self->_get_int( $self->{PBAS1} ) + $self->{_mac_header};
268 1         3 $self->{PBAS2} = $self->_get_int( $self->{PBAS2} ) + $self->{_mac_header};
269 1         2 $self->{SMPL} += $self->{_mac_header};
270             }
271              
272             sub _set_base_calls {
273 1     1   1 my $self = shift;
274 1         1 my $buf;
275 1         2 my $length = $self->{_seq_length} * 2;
276 1         1 my $fh = $self->{_fh};
277 1         6 seek( $fh, $self->{PLOC}, 0 );
278 1         7 read( $fh, $buf, $length );
279 1         121 @{ $self->{_basecalls} } = unpack( "n" x $length, $buf );
  1         54  
280              
281             # print "@{$self->{_basecalls}}" , "\n";
282             }
283              
284             sub _set_corrected_base_calls {
285 1     1   2 my $self = shift;
286 1         2 my $buf;
287 1         1 my $length = $self->{_seq_length_corrected} * 2;
288 1         2 my $fh = $self->{_fh};
289 1         6 seek( $fh, $self->{PLOC1}, 0 );
290 1         16 read( $fh, $buf, $length );
291 1         78 @{ $self->{_basecalls_corrected} } = unpack( "n" x $length, $buf );
  1         59  
292             }
293              
294             sub _set_seq {
295 1     1   1 my $self = shift;
296 1         2 my $buf;
297 1         1 my $length = $self->{_seq_length};
298 1         2 my $fh = $self->{_fh};
299 1         6 seek( $fh, $self->{PBAS2}, 0 );
300 1         8 read( $fh, $buf, $length );
301 1         2 $self->{_sequence} = $buf;
302              
303             #my @seq = unpack( "C" x $length, $buf);
304             #print $buf, "\n";
305             }
306              
307             sub _set_corrected_seq {
308 1     1   1 my $self = shift;
309 1         1 my $buf;
310 1         2 my $length = $self->{_seq_length_corrected};
311 1         1 my $fh = $self->{_fh};
312 1         6 seek( $fh, $self->{PBAS1}, 0 );
313 1         7 read( $fh, $buf, $length );
314 1         2 $self->{_sequence_corrected} = $buf;
315             }
316              
317             sub _set_traces {
318 1     1   1 my $self = shift;
319 1         5 my $buf;
320 1         1 my ( @pointers, @A, @G, @C, @T );
321 1         3 my (@datas) =
322             ( $self->{DATA9}, $self->{DATA10}, $self->{DATA11}, $self->{DATA12} );
323 1         2 my $fh = $self->{_fh};
324 1         6 seek( $fh, $self->{FWO}, 0 );
325 1         5 read( $fh, $buf, 4 );
326 1         5 my @order = split( //, $buf );
327              
328             #print "@order", "\n";
329 1         3 for ( my $i = 0 ; $i < 4 ; $i++ ) {
330 4 100       23 if ( $order[$i] =~ /A/i ) {
    100          
    100          
    50          
331 1         2 $pointers[0] = $datas[$i];
332             } elsif ( $order[$i] =~ /C/i ) {
333 1         2 $pointers[1] = $datas[$i];
334             } elsif ( $order[$i] =~ /G/i ) {
335 1         3 $pointers[2] = $datas[$i];
336             } elsif ( $order[$i] =~ /T/i ) {
337 1         2 $pointers[3] = $datas[$i];
338             } else {
339 0         0 croak "Wrong traces\n";
340             }
341             }
342 1         3 for ( my $i = 0 ; $i < 4 ; $i++ ) {
343 4         35 seek( $fh, $pointers[$i], 0 );
344 4         130 read( $fh, $buf, $self->{_trace_length} * 2 );
345 4 100       12 if ( $i == 0 ) {
346 1         1920 @A = unpack( "n" x $self->{_trace_length}, $buf );
347             }
348 4 100       169 if ( $i == 1 ) {
349 1         2232 @C = unpack( "n" x $self->{_trace_length}, $buf );
350             }
351 4 100       310 if ( $i == 2 ) {
352 1         1466 @G = unpack( "n" x $self->{_trace_length}, $buf );
353             }
354 4 100       211 if ( $i == 3 ) {
355 1         1753 @T = unpack( "n" x $self->{_trace_length}, $buf );
356             }
357             }
358 1         35 @{ $self->{A} } = @A;
  1         607  
359 1         34 @{ $self->{G} } = @G;
  1         952  
360 1         33 @{ $self->{T} } = @T;
  1         903  
361 1         38 @{ $self->{C} } = @C;
  1         1893  
362             }
363              
364             sub _get_int {
365 8     8   9 my $self = shift;
366 8         7 my $buf;
367 8         8 my $pos = shift;
368 8         9 my $fh = $self->{_fh};
369 8         53 seek( $fh, $pos, 0 );
370 8         39 read( $fh, $buf, 4 );
371 8         32 return unpack( "N", $buf );
372             }
373              
374             sub _set_max_trace {
375 1     1   3 my $self = shift;
376 1         2 my @A = @{ $self->{A} };
  1         582  
377 1         3 my @T = @{ $self->{T} };
  1         547  
378 1         4 my @G = @{ $self->{G} };
  1         525  
379 1         3 my @C = @{ $self->{C} };
  1         510  
380 1         5 my $max = 0;
381 1         11 for ( my $i = 0 ; $i < @T ; $i++ ) {
382 8853 100       13873 if ( $T[$i] > $max ) { $max = $T[$i]; }
  6         9  
383 8853 50       14707 if ( $A[$i] > $max ) { $max = $A[$i]; }
  0         0  
384 8853 50       15683 if ( $G[$i] > $max ) { $max = $G[$i]; }
  0         0  
385 8853 100       22472 if ( $C[$i] > $max ) { $max = $C[$i]; }
  1         4  
386             }
387 1         783 $self->{_maximum_trace} = $max;
388             }
389              
390             sub _set_sample_name {
391 1     1   4 my $self = shift;
392 1         3 my $buf;
393 1         7 my $fh = $self->{_fh};
394 1         22 seek( $fh, $self->{SMPL}, 0 );
395 1         29 read( $fh, $buf, 1 );
396 1         7 my $length = unpack( "C", $buf );
397 1         3 read( $fh, $buf, $length );
398 1         6 $self->{_sample} = $buf;
399             }
400              
401             =head1 METHODS
402              
403             =head2 get_max_trace()
404              
405             Title : get_max_trace()
406             Usage : $max = $abi->get_max_trace();
407             Function : Returns the maximum trace value of all the traces.
408             Args : Nothing
409             Returns : A scalar
410              
411             =cut
412              
413             sub get_max_trace {
414 1     1 1 8179 my $self = shift;
415 1         9 return $self->{_maximum_trace};
416             }
417              
418             =head2 get_trace()
419              
420             Title : get_trace()
421             Usage : my @a = $abi->get_trace("A");
422             Function : Returns the raw traces as array.
423             Args : "A" or "G" or "C" or "T"
424             Returns : An array
425              
426             =cut
427              
428             sub get_trace {
429 4     4 1 269155 my $self = shift;
430 4         13 my $symbol = shift;
431 4 100       56 if ( $symbol =~ /A/i ) {
    100          
    100          
    50          
432 1         4 return @{ $self->{A} };
  1         994  
433             } elsif ( $symbol =~ /G/i ) {
434 1         3 return @{ $self->{G} };
  1         1600  
435             } elsif ( $symbol =~ /C/i ) {
436 1         4 return @{ $self->{C} };
  1         1293  
437             } elsif ( $symbol =~ /T/i ) {
438 1         4 return @{ $self->{T} };
  1         1538  
439             } else {
440 0         0 croak "Illegal symbol\n";
441             }
442             }
443              
444             =head2 get_sequence()
445              
446             Title : get_sequence()
447             Usage : my $seq = $abi->get_sequence();
448             Function : Returns the original unedited sequence as string. If you want to access the edited
449             sequence use "get_corrected_sequence()" instead.
450             Args : Nothing
451             Returns : A scalar
452              
453             =cut
454              
455             sub get_sequence {
456 1     1 1 2 my $self = shift;
457 1         6 return $self->{_sequence};
458             }
459              
460             =head2 get_corrected_sequence()
461              
462             Title : get_corrected_sequence()
463             Usage : my $seq = $abi->get_corrected_sequence();
464             Function : Returns the corrected sequence as string. If you want to access the original
465             unedited sequence, use "get_sequence()" instead.
466             Args : Nothing
467             Returns : A scalar
468              
469             =cut
470              
471             sub get_corrected_sequence {
472 1     1 1 7422 my $self = shift;
473 1         8 return $self->{_sequence_corrected};
474             }
475              
476             =head2 get_sequence_length()
477              
478             Title : get_sequence_length()
479             Usage : my $seq_length = $abi->get_sequence_length();
480             Function : Returns the sequence length of the orginal unedited sequence.
481             Args : Nothing
482             Returns : A scalar
483              
484             =cut
485              
486             sub get_sequence_length {
487 1     1 1 4 my $self = shift;
488 1         6 return $self->{_seq_length};
489             }
490              
491             =head2 get_corrected_sequence_length()
492              
493             Title : get_corrected_sequence_length()
494             Usage : my $seq_length = $abi->get_corrected_sequence_length();
495             Function : Returns the length of the edited sequence.
496             Args : Nothing
497             Returns : A scalar
498              
499             =cut
500              
501             sub get_corrected_sequence_length {
502 1     1 1 3 my $self = shift;
503              
504             #print STDERR "**ABI**",$self->{_seq_length_corrected},"\n";
505 1         6 return $self->{_seq_length_corrected};
506             }
507              
508             =head2 get_trace_length()
509              
510             Title : get_trace_length()
511             Usage : my $trace_length = $abi->get_trace_length();
512             Function : Returns the trace length
513             Args : Nothing
514             Returns : A scalar
515              
516             =cut
517              
518             sub get_trace_length {
519 1     1 1 99156 my $self = shift;
520 1         7 return $self->{_trace_length};
521             }
522              
523             =head2 get_base_calls()
524              
525             Title : get_base_calls()
526             Usage : my @base_calls = $abi->get_base_calls();
527             Function : Returns the called bases by the base caller. This method will return the unedited
528             original basecalls created by the basecaller.
529             Args : Nothing
530             Returns : An array
531              
532             =cut
533              
534             sub get_base_calls {
535 1     1 1 998 my $self = shift;
536 1         3 return @{ $self->{_basecalls} };
  1         70  
537             }
538              
539             =head2 get_corrected_base_calls()
540              
541             Title : get_corrected_base_calls()
542             Usage : my @base_calls = $abi->get_corrected_base_calls();
543             Function : If you have edited the trace file you can get the corrected base call
544             with this method
545             Args : Nothing
546             Returns : An array
547              
548             =cut
549              
550             sub get_corrected_base_calls {
551 1     1 1 6 my $self = shift;
552 1         2 return @{ $self->{_basecalls_corrected} };
  1         123  
553             }
554              
555             =head2 get_sample_name()
556              
557             Title : get_sample_name()
558             Usage : my $sample = $abi->get_sample_name();
559             Function : Returns hard coded sample name
560             Args : Nothing
561             Returns : A scalar
562              
563             =cut
564              
565             sub get_sample_name {
566 1     1 1 2 my $self = shift;
567 1         8 return $self->{_sample};
568             }
569              
570             =head1 AUTHOR
571              
572             Malay
573              
574             =head1 BUGS
575              
576             Please report any bugs or feature requests to
577             C, or through the web interface at
578             L.
579             I will be notified, and then you'll automatically be notified of progress on
580             your bug as I make changes.
581              
582             or
583              
584             You can directly contact me to my email address.
585              
586              
587             =head1 SUPPORT
588              
589             You can find documentation for this module with the perldoc command.
590              
591             perldoc ABI
592              
593             You can also look for information at:
594              
595             =over 4
596              
597             =item * AnnoCPAN: Annotated CPAN documentation
598              
599             L
600              
601             =item * CPAN Ratings
602              
603             L
604              
605             =item * RT: CPAN's request tracker
606              
607             L
608              
609             =item * Search CPAN
610              
611             L
612              
613             =back
614              
615              
616             =head1 COPYRIGHT & LICENSE
617              
618             Copyright 2002,2006 Malay, all rights reserved.
619              
620             This program is free software; you can redistribute it and/or modify it
621             under the same terms as Perl itself.
622              
623             =cut
624              
625             1;