in reply to Morse code via sound card input

Reading code will be trickier, since you will need to detect silences (as opposed to signal) and time them. Here is a start, which will display a number between 0 and ~65500, depending on what your soundcard is playing. Start it, and play a sound and you should see the numbers and 0 for silence , also see a VU-Meter based on this below: UPDATE: Code in readmore is a noise meter, ineffective for reading tones

#!/usr/bin/perl use Audio::DSP; #alsamixer must be setup right, AC'97 capture adjusts sensitivity, #just turn it up to 100 for this to work as intended ($buf, $chan, $fmt, $rate) = (4096, 1, 16 , 8000); $dsp = new Audio::DSP(buffer => $buf, channels => $chan, format => $fmt, rate => $rate); $dsp->init() || die $dsp->errstr(); my $samples = 11500; my $num_tot = 0; my $div_tot = 0; # Record x samples of sound for (1..$samples) { #read 16 bits of raw data my $data = $dsp->dread(16) || die $dsp->errstr(); my $num = unpack( 'v', $data ); #filter out baseline noise if($num > 65000){next}else{ print "$num\n"; $num_tot += $num; $div_tot += 32768; #gives a good average } } $dsp->close(); print "\n\nAverage ",sprintf('%0.2f',$num_tot/$div_tot),"\n";

And here is the same idea, applied to a meter. You will need to setup some sort of timer, to time the 0 and non-zero segments, account for noise, and get the sender's speed. Then you need to capture the ons and offs, taking into account the sending speed, and check them against a hash of letters. It sure would be alot easier, if you can preset the sender speed. Autodetection of sender speed seems to be the big obstacle to me.

#!/usr/bin/perl use warnings; use strict; use Tk; use Audio::DSP; use constant PI => 3.1415926; #alsamixer must be setup right, AC'97 capture adjusts sensitivity, #just turn it up to 100 for this to work as intended my ($buf, $chan, $fmt, $rate) = (4096, 1, 16 , 8000); my $dsp = new Audio::DSP(buffer => $buf, channels => $chan, format => $fmt, rate => $rate); $dsp->init() || die $dsp->errstr(); my $mw = MainWindow->new; my $x = 300; my $y = 300; $mw->protocol('WM_DELETE_WINDOW' => sub { &clean_exit }); $mw->fontCreate('big', -family=>'arial', -weight=>'bold', -size=> 36 ); my $count_tot = 0; my $count_max = 0; my $c = $mw->Canvas( -width => $x, -height => $y, -bd => 2, -relief => 'sunken', -background => 'black', )->pack(); $c->createLine( $x/2, $y/2, 10 , $y/2 , -tags => ['needle'], -arrow => 'last', -width => 15, -fill => 'hotpink', ); my $gauge = $c->createArc( 10,10, $x-10,$y-10, -start => 0, -style => 'arc', -width => 5, -extent => 180, -outline => 'skyblue', -tags => ['gauge'], ); my $hub = $c->createArc( ($x/2 - 20), ($y/2 - 20) ,( $x/2 + 20) ,( $y/2 + 20), -start => 90, -extent => 359, -fill => 'lightgreen', -tags => ['hub'], ); my $text = $c->createText( $x/2, $y/2 + 150, -text => $count_max, -font => 'big', -fill => 'yellow', -anchor => 's', -tags => ['text'] ); $c->raise('needle','text'); $c->raise('hub','needle'); $mw->bind('<space>',sub{ &toggle_status }); $mw->waitVisibility; my $timer = $mw->repeat(50,sub{ my $value = &update_meter; $value = sprintf('%2.1f',$value); if($value <= 0){$value = 0 } if($value >= 100){$value = 100} my $pos = $value/100; my $x1 = $x/2 - .95*$x/2 * (cos( $pos * PI )); my $y1 = $y/2 - .95*$y/2 * (sin( $pos * PI )); $c->coords('needle', ($x/2), ($y/2), $x1, $y1); if($value > $count_max){ $count_max = $value } if($value == 0){ $count_max = 0 } $c->itemconfigure($text, -text => $value); $mw->update; }); MainLoop; ######################################################### sub update_meter { my $samples = 15; my $num_tot = 0; my $div_tot = 0; my $value =0; # Record x samples of sound for (1..$samples) { #read 16 bits of raw data my $data = $dsp->dread(16); # || die $dsp->errstr(); my $num = unpack( 'v', $data ); #filter out baseline noise if($num > 65000){next}else{ #print "$num "; $num_tot += $num; $div_tot += 32768; #gives a good average } } if( $div_tot != 0 ){ $value = ($num_tot/$div_tot) * 100; } return $value; } ###################################################################### +# sub clean_exit{ $timer->cancel; $dsp->close(); exit; } ################################################################# __END__

I'm not really a human, but I play one on earth. Cogito ergo sum a bum

Replies are listed 'Best First'.
Re^2: Morse code via sound card input
by ww (Archbishop) on Jul 07, 2007 at 18:03 UTC
    Is autodetection of the sender's speed really necessary?

    I may be way off base here, but I think there's supposed to be a distinct ratio between the duration of the dit, the duration of the dah, the duration of the intercharacter pause and the duration of the interword pause.

    If true, and the sender has a good fist, that may offer an approach to avoiding the need for autodetection.

    ...or, I may be all wet.

      Those intervals are specified in terms of ratios. The absolute intervals are a direct function of the sending speed - the ratios remain fixed, the actual intervals change according to the notional sending speed.

      The complication for detection is that not only do you need to be able to compensate for a drift in sending speed over time, but also you have to accommodate deviations from the ideal ratios of the various intervals which may occur on a letter by letter basis, and may also change depending on the context of individual letters.

      Autospeed detection is actually essential for a practical decoder because the speed does change over time. A hint can help, but it's unlikely to be enough on its own.


      DWIM is Perl's answer to Gödel
      This would be an interesting project to test. I mean I could take the code I showed above, and while sampling every 10 milliseconds for a 0 or 1 condition, draw a line on a Tk canvas and see what it might look like, and whether I could produce a program to convert it to letters. I would expect it to look something like Morse code, but the width of each on-state would have to be put through some artificial intelligence program to detect whether it was a dit or dash. Then there is the problem of the silence between letters, and it's duration, compared to the silence within a sent letter. For instance, How would I know whether a single "dit dit" was sent, rather than 2 sucessive "dits"?

      It all seems doable, but what happens if the sender's speed and intervals subtlely change over time? So now the AI code needs to keep running averages of the sender's current delay-intervals.

      All possible, but it would be pretty complex code to gaurantee good transcription accuracy.


      I'm not really a human, but I play one on earth. Cogito ergo sum a bum