#!/usr/bin/perl # Copyright 2011 - David Westbrook - KJ4IZW # Freeware for non-commerical use =pod Translate string to send: * Insert FIGS shift at any Letter-to-Figure boundry * Insert LTRS shift at any Figure-to-Letter boundry * if USOS: insert FIGS shift for space-to-Figure * if USOS: remove shift for any space-LTRS instances * Flag any illegal characters Count characters: * remove illegal chars * replace LTRS and FIGS as single temp chars * take string length Calculate time: * time_sec = char_count * (5 + 1 + STOPBITS) bits/char: 5 data, 1 start, N stop / 45.5 bits per second ~~~~~~~~~~~~~~ References: Chapter 16 of the 2010 and 2011 ARRL Handbook. http://en.wikipedia.org/wiki/Baudot_code http://en.wikipedia.org/wiki/Asynchronous_serial_communication http://en.wikipedia.org/wiki/Serial_port#Stop_bits =cut use strict; use warnings; #use lib qw{ # /kunden/homepages/12/d93966548/htdocs/local/lib/perl/5.8.8 # /kunden/homepages/12/d93966548/htdocs/local/lib/perl/5.8.8/i486-linux-gnu-thread-multi #}; use lib qw{ /kunden/homepages/12/d93966548/htdocs/local/lib/perl/5.10.1 /kunden/homepages/12/d93966548/htdocs/local/share/perl/5.10.1 /kunden/homepages/12/d93966548/htdocs/local/lib/perl5 }; use Template; use CGI::Simple; my $q = CGI::Simple->new; if( $q->param('src') ){ print $q->header('text/plain'); open FILE, '<', $0; while(){ print }; close FILE; } my %data = ( msg => $q->param('msg') || q+ 599 0123 0123 599-0123-0123 TU 1D SC W7AY 599 599 KJ4IZW KJ4IZW kj4izw +, stop_bits => $q->param('stop_bits') || 1, usos => defined $q->param('usos') ? $q->param('usos') : 1, mode => $q->param('mode') || 'RTTY', showbitflips => $q->param('showbitflips') ? 1 : 0, ); @{$data{lines}} = map { process_string( $_, $data{stop_bits}, $data{usos} ) } grep { length $_ } split /[\r\n]+/, $data{msg}; if( $data{mode} eq 'PSK31' && $q->param('showbitflips') ){ my %char2varicode = varicode_mapping(); my %varicode2char; @varicode2char{ values %char2varicode } = keys %char2varicode; @{$data{lines}} = map { my $line = $_; my $psk = $line->{psk_sent}; my @alt_msgs; foreach my $i ( 0 .. length($psk)-1 ){ my $s = [ split '', $psk ]; $s->[$i] = $s->[$i] ? 0 : 1; $s = join '', map { exists $varicode2char{$_} ? $varicode2char{$_} : chr(164) } split /00+/, join('',@$s); push @alt_msgs, $s; } ( $line, map { process_string( $_, $data{stop_bits}, $data{usos} ) } @alt_msgs ) } @{$data{lines}}; } $_->{msg_html} = color_code($q, $_->{msg}) for @{$data{lines}}; $_->{sent_html} = color_code($q, $_->{sent}) for @{$data{lines}}; $_->{psk_html} = do { my $s = $_->{psk_sent}; $s =~ s#00#$&#g; $s } for @{$data{lines}}; $_->{cw_html} = do { my $s = $_->{cw_sent}; $s =~ s#[^-. _:]+#$&#g; $s =~ s#:# #g; $s =~ s#_# #g; $s =~ s#(?<=[-.])[-.]#$&#g; $s } for @{$data{lines}}; print $q->header; my $template = Template->new; $template->process( \*DATA, \%data ) or print $template->error; exit; ################################################################## sub color_code { my ($q, $s) = @_; $s = $q->escapeHTML($s); $s =~ s#<FIGS>#%#g; $s =~ s#<LTRS>#@#g; $s =~ s#<space># #g; $s =~ s#<BAD:(.{1,8})>#$1#g; $s =~ s#\xA4#$&#g; # chr(164) return $s; } sub process_string { my ($msg, $stop_bits, $usos) = @_; ######################################################################## my $sent = uc $msg; # Bad chars: * % @ [ ] { } \+ = \\ | ` ~ < > my $reL = q{AQWERTYUIOPSDFGHJKLZXCVBNM} . "\r\n"; # . ($usos?'':' '); my $reF = q{-1234567890'$!&#'()"/:;?,.} . "\r\n"; # . ($usos?'':' '); my $charset = $reF . $reL . ' '; # note: make sure that any "-" is first in the charsets, otherwise becomes a range. $sent =~ s/((?:[$reL]|^) *)([$reF])/${1}${2}/sg; $sent =~ s/((?:[$reF]|^) *)([$reL])/${1}${2}/sg; # functional; but not worrying about newlines at the moment. ## Remove excess shifts, e.g. LTRS-newline-FIGS (or LTRS-space-FIGS if USOS off) # $sent =~ s/([\r\n])/$1/sg; ## Remove any shifts at very end # $sent =~ s/(|)(?:[\r\n]+)$//s; if( $usos ){ # since LTRS after space is implicit. $sent =~ s/( )([$reF])/$1$2/sg; $sent =~ s/ / /g; } $sent =~ s/(?)//sg; $sent =~ s/ //g; my $sent_length = length do { local $_=$sent; s/<(LTRS|FIGS|space)>/+/g; s///sg; $_ }; my $sent_time = sprintf '%.2f', # ==> time in seconds. $sent_length # char * (5 + 1 + $stop_bits) # bits/char: data + start + stop / 45.5 # bits/sec ; ######################################################################## ######################################################################## # http://en.wikipedia.org/wiki/PSK31#Technical_information my %refV = varicode_mapping(); my @L = map { $refV{$_} } split //, $msg ; my $psk_bits = 2 * ( scalar(@L) - 1 ); # two 0's between each letter. $psk_bits += length $_ for @L; my $psk_time = sprintf '%.2f', 0.032 * $psk_bits; # 32ms per bit; time in seconds ######################################################################## ######################################################################## # http://en.wikipedia.org/wiki/Morse_code # dot 1 unit # dash 3 units # parts spacing 1 unit # letter spacing 3 units # word spacing 7 units ### my %refCW = cw_mapping(); my @WORDSCW = split /( +)/, $msg; my $cw_units = 0; $cw_units += 7 * length($_) for grep { / / } @WORDSCW; # each space is 7 units $cw_units += 3 * (length($_)-1) for grep { /[^ ]/ } @WORDSCW; # for N-letter word, N-1 spacings between letters, at 3 units each. my @LETTERSCW = map { $refCW{uc $_} || $_ } split //, $msg ; my @LCW = map { split //, $_ } @LETTERSCW; $cw_units += 1 * scalar grep { $_ eq '.' } @LCW; # 1 unit per dot $cw_units += 3 * scalar grep { $_ eq '-' } @LCW; # 3 units per dash $cw_units += 1 * (length($_)-1) for grep { /[^ ]/ } @LETTERSCW; # for N-signal letter, N-1 spacings between signals, at 1 unit each. my $cw_sent = join '_', @LETTERSCW; $cw_sent =~ s/(_ )/ /g; $cw_sent =~ s/( _)/ /g; $cw_sent =~ s/ /:/g; my $cw_signal_ct = scalar grep { /[-.]/ } @LCW; my $cw_time = sprintf '%.2f', 0.060 * $cw_units; # 60ms per unit for 20WPM; time in seconds ######################################################################## #use Data::Dumper;do{print "Content-type: text/plain\n\n"; print Dumper [ $msg, $cw_signal_ct, $cw_time, $cw_units, $cw_sent, \@WORDSCW ]; exit} if $msg =~ /n8cl/;# $q->param('mode') eq 'CW'; return { msg => $msg, sent => $sent, msg_length => length($msg), sent_length => $sent_length, sent_time => $sent_time, psk_length => $psk_bits, psk_time => $psk_time, psk_sent => join('00', @L), # two 0's between each letter cw_length => $cw_units, cw_time => $cw_time, cw_sent => $cw_sent, cw_ct => $cw_signal_ct, } } sub varicode_mapping { return ( # Glyph => Varicode # http://en.wikipedia.org/wiki/Varicode#Printable_characters ' ' => '1', '!' => '111111111', '"' => '101011111', '#' => '111110101', '$' => '111011011', '%' => '1011010101', '&' => '1010111011', "'" => '101111111', '(' => '11111011', ')' => '11110111', '*' => '101101111', '+' => '111011111', ',' => '1110101', '-' => '110101', '.' => '1010111', '/' => '110101111', '0' => '10110111', '1' => '10111101', '2' => '11101101', '3' => '11111111', '4' => '101110111', '5' => '101011011', '6' => '101101011', '7' => '110101101', '8' => '110101011', '9' => '110110111', ':' => '11110101', ';' => '110111101', '<' => '111101101', '=' => '1010101', '>' => '111010111', '?' => '1010101111', '@' => '1010111101', 'A' => '1111101', 'B' => '11101011', 'C' => '10101101', 'D' => '10110101', 'E' => '1110111', 'F' => '11011011', 'G' => '11111101', 'H' => '101010101', 'I' => '1111111', 'J' => '111111101', 'K' => '101111101', 'L' => '11010111', 'M' => '10111011', 'N' => '11011101', 'O' => '10101011', 'P' => '11010101', 'Q' => '111011101', 'R' => '10101111', 'S' => '1101111', 'T' => '1101101', 'U' => '101010111', 'V' => '110110101', 'W' => '101011101', 'X' => '101110101', 'Y' => '101111011', 'Z' => '1010101101', '[' => '111110111', '\\' => '111101111', ']' => '111111011', '^' => '1010111111', '_' => '101101101', '`' => '1011011111', 'a' => '1011', 'b' => '1011111', 'c' => '101111', 'd' => '101101', 'e' => '11', 'f' => '111101', 'g' => '1011011', 'h' => '101011', 'i' => '1101', 'j' => '111101011', 'k' => '10111111', 'l' => '11011', 'm' => '111011', 'n' => '1111', 'o' => '111', 'p' => '111111', 'q' => '110111111', 'r' => '10101', 's' => '10111', 't' => '101', 'u' => '110111', 'v' => '1111011', 'w' => '1101011', 'x' => '11011111', 'y' => '1011101', 'z' => '111010101', '{' => '1010110111', '|' => '110111011', '}' => '1010110101', '~' => '1011010111', ); } sub cw_mapping { return ( ' ' => ' ', 'A' => '.-', 'B' => '-...', 'C' => '-.-.', 'D' => '-..', 'E' => '.', 'F' => '..-.', 'G' => '--.', 'H' => '....', 'I' => '..', 'J' => '.---', 'K' => '-.-', 'L' => '.-..', 'M' => '--', 'N' => '-.', 'O' => '---', 'P' => '.--.', 'Q' => '--.-', 'R' => '.-.', 'S' => '...', 'T' => '-', 'U' => '..-', 'V' => '...-', 'W' => '.--', 'X' => '-..-', 'Y' => '-.--', 'Z' => '--..', '0' => '-----', '1' => '.----', '2' => '..---', '3' => '...--', '4' => '....-', '5' => '.....', '6' => '-....', '7' => '--...', '8' => '---..', '9' => '----.', '.' => '.-.-.-', ',' => '--..--', '?' => '..--..', "'" => '.----.', '!' => '-.-.--', '/' => '-..-.', '(' => '-.--.', ')' => '-.--.-', '&' => '.-...', ':' => '---...', ';' => '-.-.-.', '=' => '-...-', '+' => '.-.-.', '-' => '-....-', '_' => '..--.-', '"' => '.-..-.', '$' => '...-..-', '@' => '.--.-.', ); } __DATA__ RTTY Message Length Calculator

[% mode %] Message Length Calculator

Other modes: RTTY | PSK31 | CW

David KJ4IZW

Enter lines of exchanges to compare:

[% IF mode == 'RTTY' %] Stop Bits:
Un-Shift-On-Space (USO):
[% END %] [% IF mode == 'PSK31' %] Show all single-bit-flip variations.
[% END %]
[% IF mode == 'RTTY' %]
Baudot Characters:
LTRS:QWERTYUIOPASDFGHJKLZXCVBNM
FIGS:1234567890-'$!&#'()"/:;?,.
[% END %]
[% IF mode == 'RTTY' %]

RTTY

[% USE HTML %] [% FOREACH line = lines %] [% END %]
Text
Message
Time
(sec)
Text
Length
Encoded
Length
Encoded Message
   @=LTRS shift    %=FIGS shift    space   
[%# HTML.escape(line.msg) _ '
' %] [% line.msg_html %]
[% line.sent_time %] [% line.msg_length %] [% line.sent_length %] [%# HTML.escape(line.sent) _ '
' %] [% line.sent_html %]
Stop Bits: [% stop_bits %]           USOS: [% usos ? 'On' : 'Off' %]           45.5 baud
[%END %] [% IF mode == 'PSK31' %]

BPSK31

[% USE HTML %] [% FOREACH line = lines %] [% END %]
Text
Message
Text
Length
Time
(sec)
Varicode
Length
Varicode
Message
[%# HTML.escape(line.msg) _ '
' %] [% line.msg_html %]
[% line.msg_length %] [% line.psk_time %] [% line.psk_length %] [% line.psk_html %]
[%END %] [% IF mode == 'CW' %]

Morse code

Times based on 60ms units (20WPM). [% USE HTML %] [% FOREACH line = lines %] [% END %]
Text
Message
Text
Length
Time
(sec)
CW
Units
CW
Signals
CW
Message
[%# HTML.escape(line.msg) _ '
' %] [% line.msg_html %]
[% line.msg_length %] [% line.cw_time %] [% line.cw_length %] [% line.cw_ct %] [% line.cw_html %]
[%END %]

View source code & comments (written in perl).

[% IF mode == 'RTTY' %] Thanks to Kok Chen W7AY for the calculator idea on the RTTY reflector and technical assistance via email.
[% END %]
David Westbrook - KJ4IZW - dwestbrook@gmail.com
Content-Type: text/html; charset=ISO-8859-1 RTTY Message Length Calculator

RTTY Message Length Calculator

Other modes: RTTY | PSK31 | CW

David KJ4IZW

Enter lines of exchanges to compare:

Stop Bits:
Un-Shift-On-Space (USO):
Baudot Characters:
LTRS:QWERTYUIOPASDFGHJKLZXCVBNM
FIGS:1234567890-'$!&#'()"/:;?,.

RTTY

Text
Message
Time
(sec)
Text
Length
Encoded
Length
Encoded Message
   @=LTRS shift    %=FIGS shift    space   
599 0123 0123 2.46 13 16 %599 %0123 %0123
599-0123-0123 2.15 13 14 %599-0123-0123
TU 1D SC 1.69 8 11 @TU %1@D SC
W7AY 599 599 KJ4IZW 4.15 21 27 W%7@AY %599 %599 KJ%4@IZW
KJ4IZW 1.38 6 9 @KJ%4@IZW
kj4izw 1.38 6 9 @KJ%4@IZW
Stop Bits: 1           USOS: On           45.5 baud


View source code & comments (written in perl).

Thanks to Kok Chen W7AY for the calculator idea on the RTTY reflector and technical assistance via email.
David Westbrook - KJ4IZW - dwestbrook@gmail.com