Code covered by the BSD License  

Highlights from
Morse Code Practice System

image thumbnail
from Morse Code Practice System by James Willmann
GUI allows the operator to send the Morse Code of the alphabet, a text string or from the keyboard.

MorseCode.m
function MorseCode
%MORSECODE Morse Code Player
% Morse code can be sent in three ways:  
% Transmit Alphabet, 
% Transmit From File and 
% Transmit From Keyboard. 
% The code speed(rate of dots and dashes), word speed
% (period between words) and audio frequency of transmitted 
% dots and dashes can be adjusted.  The word speed is 
% restricted to be equal or less than the code speed.  The code 
% and word speed can be adjusted either by the sliders or by 
% entering values in the respective text fields.
%
% When transmitting either a text file or alphabet 
% there is a stop transmission button to allow the operator to 
% terminate the transmission.
% 
% When transmitting a file the user must first, using 
% the "Select TextFile" from the menu bar, select a file to be 
% transmitted.  The transmission will stop when the end of the 
% file is reached. All text in the file is converted to upper 
% case as it is being used.
%
% When the Transmit Keyboard is selected a small modal 
% window opens and retains focus until the transmission is 
% ended.  The transmission is ended by pressing the escape key. 
%  The inputted text is buffered and transmitted at the 
% selected rates.  When entering the text from the keyboard, 
% pressing the backspace key will delete the last character 
% entered from the input buffer.
%
% From the menu bar selecting "Code Table" will display the 
% code table built into the program.  There are 60 entries in 
% the table with the last being a word space.
%
% General Design of the Program
%
% The core of the program is the CodeTable.  The function BuildCodeTable 
% creates a 60x4 cell array where the first 3 columns are text and contain 
% the character, dots and dashes, dits and daws.  The fourth column is 
% reserved for a wave file that represents the Morse code for that character 
% at the selected speed.
%
% After the GUI is constricted, to initialize the program the function 
% FillCodeTable is called.  This function first calls BuildCodeTable to 
% populate the first three columns of the CodeTable.  Then this function 
% builds the dots dashes and spaces for the selected code and word speed 
% by calling the function MakeWave to build the wave file for a dot, dash 
% and spaces.  Then MakeCharacter is called to build the wave file for each 
% character.  This result is stored in the fourth column of the CodeTable 
% for each of the character entries in the table.
%
% When either the speed or frequency is changed, this process is repeated so 
% there is a new CodeTable constructed to use with the new settings.  The 
% process of transmission consist of taking each character of the input 
% stream and sending it to the standard audio output using waveplay.  
% Because waveplay does not return until the sample is sent, when sending 
% the alphabet or a loaded text string the functions XmitAlphabetCallback 
% and XmitFileCallback just loop through all of the characters until completed.
%
% Transmitting from the keyboard is a bit more complicated.  The 
% XmitKbdCallback starts a timer task and opens a small modal window 
% activating the KeyPressFunction which links to GetKeyCallback.  At 
% GetKeyCallback each keyboard entry is put into a 2xn cell array 
% InputString.  In row 1 is the character and in row 2 is the wave file 
% for that character.  This is continued until the operator presses escape.  
% Then the character string 'end' in placed in the character slot and the 
% keyboard entry process is terminated.
% 
% All the while this input is going on the timer task, TimerTaskCallback, 
% is looking at the InputString to see if there is a new character.  If 
% there is, it is transmitted, and the program does not return until the 
% transmission is complete.  Then the timer task continues and repeats this 
% process until it finds the string 'end' and then terminates.
 
%%   Set start up values
    
    IsFile = 0;                 %   This is set to 1 when a file to transmit is loaded
    StopXmit = 0;               %   Set to 1 when operator wants to stop the transmission
    TextToSend = [];            %   Text file built from text input file
    KbdHandle = 0;              %   Set to Kbd figure handle when Xmit from Kbd is started
    AlphabetInUse = 0;          %   Set to 1 when alphabet is being transmitted
    FileXmitInUse = 0;          %   Set to 1 when text file is being transmitted
    KbdXmitInUse = 0;           %   Set to 1 when keyboard input is being transmitted
    CharacterCount = 0;         %   Use this to count input characters
    SentCount = 0;              %   Use this to count output characters
    TimerHandle = 0;            %   Set to handle value when timer is started
    InputString = cell(2,1);    %   Set up and clear input array
    
%   Frequency list for the frequency drop down list    
    FrequencyList = {'Frequency' '300' '400' '500' '600' '700' ...
        '800' '900' '1000' '1200' '1400'};
   
%   Set default start up speed and min and max speed
    StartSpeed = 15;
    MaxSpeed = 25;
    MinSpeed = 5;
    CodeSpeed = StartSpeed;
    WordSpeed = StartSpeed/2;
    if WordSpeed < MinSpeed
        WordSpeed = MinSpeed;
    end
    
%%  Set figure and uicontrol variables    
%   Get user screen size
    SC = get(0, 'ScreenSize');
    MaxMonitorX = SC(3);
    MaxMonitorY = SC(4);

%   Set the figure window size values.  These are adjusted to account for
%   the users screen size.
    
    GUIScale = .7;                          %   Percent of screen filled by the GUI   
    MaxWindowX = GUIScale*MaxMonitorX;      %   Width of GUI      
    MaxWindowY = GUIScale*MaxMonitorY;      %   Height of GUI
    XBorder = (1-GUIScale)*MaxWindowX/2;    %   Offset from left
    YBorder = (1-GUIScale)*MaxWindowY/2;    %   Offset from bottom
    TextFont = round(MaxMonitorY/200)+6;
    TextHeight = 2.4*TextFont;
    HeaderFont = round(MaxMonitorY/200)+12;
    HeaderWidth = MaxWindowX/3;
    HeaderHeight = 2.4*HeaderFont;
    VerticalHeight = (MaxWindowY-3*HeaderHeight);
    VerticalSpace = VerticalHeight/15;
    HorzontalSpace = MaxWindowY/11;
    ActionPushbuttonWidth = MaxWindowX/5;
    
    
%   Set the color varables
    Green = [.255 .627 .225];     % Dark Green
    White = [1  1  1];            % White
    
%%  Build the main figure window with header

%   figure window
    handles.GUIFigHandle = figure(...
        'Units', 'pixels',...
        'Toolbar', 'none',...
        'Position',[ XBorder, YBorder, MaxWindowX, MaxWindowY ],...
        'NumberTitle', 'off',...
        'Name', 'Morse Code Practice System',...
        'MenuBar', 'none',...
        'Resize', 'off',...
        'DockControls', 'off',...
        'Color', White);
      
%   Set up Application title
    uicontrol('Style', 'text',...
        'Position', [ MaxWindowX/2-HeaderWidth/2 ...
                MaxWindowY-2*HeaderHeight ...
                HeaderWidth HeaderHeight ],...
        'string', 'Morse Code Practice System',...
        'BackgroundColor', White,...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', HeaderFont ); 
            
%%  Create uicontrol for Code Speed value

%   Text label
    uicontrol('Style', 'text',...
        'Position', [ 2*HorzontalSpace ...
            VerticalHeight-1*VerticalSpace ...
            2*HorzontalSpace TextHeight ],...
        'string', 'Code Speed',...
        'BackgroundColor', White,...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont ); 

%   Edit text box
    handles.DisplayCodeSpeed = uicontrol('Style', 'edit',...
        'Position', [ 4*HorzontalSpace ...
            VerticalHeight-1*VerticalSpace ...
            HorzontalSpace TextHeight ] ,...
        'string', int2str(StartSpeed),...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'callback',{@UpdateSpeedCallback, handles, 1 });

%   Slider control
    handles.SliderCodeSpeed = uicontrol('style','slider', ...
        'position',[6*HorzontalSpace ...
            VerticalHeight-1*VerticalSpace ...
            4*HorzontalSpace TextHeight], ...
        'value',(StartSpeed-MinSpeed)/(MaxSpeed-MinSpeed), ...
        'callback',{@UpdateSpeedCallback, handles, 2 });

%   Set the Hand varable so the handles will be visible when needed
    Hand.SliderCodeSpeed = handles.SliderCodeSpeed;
    Hand.DisplayCodeSpeed = handles.DisplayCodeSpeed;

%%  Create uicontrol for Word Speed value

%   Text label
    uicontrol('Style', 'text',...
        'Position', [ 2*HorzontalSpace ...
            VerticalHeight-3*VerticalSpace ...
            2*HorzontalSpace TextHeight ],...            
        'string', 'Word Speed',...
        'BackgroundColor', White,...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont ); 

%   Edit text box
    handles.DisplayWordSpeed = uicontrol('Style', 'edit',...
        'Position', [ 4*HorzontalSpace ...
            VerticalHeight-3*VerticalSpace ...
            HorzontalSpace TextHeight ],...
        'string', int2str(WordSpeed),...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'callback',{@UpdateSpeedCallback, handles, 3 }); 

%   Slider control    
    handles.SliderWordSpeed = uicontrol('style','slider', ...
        'position',[ 6*HorzontalSpace ...
            VerticalHeight-3*VerticalSpace ...
            4*HorzontalSpace TextHeight], ...
        'value',(WordSpeed-MinSpeed)/(MaxSpeed-MinSpeed), ...
        'callback',{@UpdateSpeedCallback, handles, 4 });
    
%   Set the Hand varable so the handles will be visible when needed    
    Hand.SliderWordSpeed = handles.SliderWordSpeed;
    Hand.DisplayWordSpeed = handles.DisplayWordSpeed;

%%  Create uicontrol drop down to select frequency

    uicontrol('Style', 'text',...
        'Position', [ 12*HorzontalSpace ...
            VerticalHeight-1.5*VerticalSpace ...
            3*HorzontalSpace TextHeight ],...            
        'string', 'Select Frequency',...
        'BackgroundColor', White,...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont ); 
    

    handles.SelectFrequency = uicontrol('Style', 'popupmenu',...
        'Position', [ 12.5*HorzontalSpace ...
            VerticalHeight-2.5*VerticalSpace ...
            HorzontalSpace TextHeight ] ,...        
        'String', FrequencyList,...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'Callback', {@SelectFrequencyCallback, handles }) ;
    
%%  Set up the Transmitted Character Displays    
%   Transmitted Character Display
    
    uicontrol('Style', 'text',...
        'Position', [ 1.8*HorzontalSpace ...
            VerticalHeight-6.5*VerticalSpace ...
            2.1*HorzontalSpace 3*TextHeight ],...            
        'string', 'Character being Transmitted',...
        'BackgroundColor', White,...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont ); 
    
    handles.XmitCharacter = uicontrol('Style', 'text',...
        'Position', [ 4*HorzontalSpace ...
            VerticalHeight-5.7*VerticalSpace ...
            HorzontalSpace 2*TextHeight ],...            
        'string', ' ',...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'ForegroundColor', Green,...
        'FontSize', 2.5*TextFont ); 
    
%   Transmitted Character String Display
    
    uicontrol('Style', 'text',...
        'Position', [ 6*HorzontalSpace ...
            VerticalHeight-6.5*VerticalSpace ...
            2*HorzontalSpace 3*TextHeight ],...            
        'string', 'String Being Transmitted',...
        'BackgroundColor', White,...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont ); 
    
        SentString = ' ';
    handles.XmitString = uicontrol('Style', 'text',...
        'Position', [ 8.2*HorzontalSpace ...
            VerticalHeight-5.7*VerticalSpace ...
            7*HorzontalSpace 2.3*TextHeight ],...            
        'string', SentString,...
        'HorizontalAlignment', 'left',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'ForegroundColor', Green,...
        'FontSize', TextFont ); 
    
%%  Set up the Action Pushbuttons

%   Transmit Alphabet pushbutton
    handles.XmitAlphabet = uicontrol('Style', 'pushbutton',...
        'Position', [ .5*ActionPushbuttonWidth ...
            VerticalHeight-8*VerticalSpace ...
            ActionPushbuttonWidth 2*TextHeight ] ,...
        'string', 'Transmit Alphabet',...
        'ForegroundColor', Green,...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'Callback', {@XmitAlphabetCallback, handles } );

%   Transmit File pushbutton
    handles.XmitFile = uicontrol('Style', 'pushbutton',...
        'Position', [ 2*ActionPushbuttonWidth ...
            VerticalHeight-8*VerticalSpace ...
            ActionPushbuttonWidth 2*TextHeight ] ,...
        'string', 'Transmit File',...
        'ForegroundColor', Green,...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'Callback', {@XmitFileCallback, handles } );
    
%   Transmit from Keyboard pushbutton
    handles.XmitKbd = uicontrol('Style', 'pushbutton',...
        'Position', [ 3.5*ActionPushbuttonWidth ...
            VerticalHeight-8*VerticalSpace ...
            ActionPushbuttonWidth 2*TextHeight ] ,...
        'string', 'Transmit Keyboard',...
        'ForegroundColor', Green,...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'Callback', {@XmitKbdCallback, handles } );
    
%   Stop pushbutton
    uicontrol('Style', 'pushbutton',...
        'Position', [ 2*ActionPushbuttonWidth ...
            VerticalHeight-11*VerticalSpace ...
            ActionPushbuttonWidth 2*TextHeight ] ,...
        'string', 'Stop Transmission',...
        'ForegroundColor', Green,...
        'HorizontalAlignment', 'center',...
        'FontName', 'arial',...
        'FontWeight', 'bold',...
        'FontSize', TextFont,...
        'Callback', {@StopXmitCallback, handles } );
       
%%  Build the drop down menu

%   No comment necessary
        uimenu('Label', '| Select Text File |',...
            'Callback', @GetTextFileCallback );
        uimenu('Label', '| Code Table |',...
            'Callback', {@DisplayCodeTableCallback, handles });
        uimenu('Label', '|  Help  |',...
            'Callback', {@HelpAndAboutCallback, handles, 1});
        uimenu('Label', '|  About  |',...
            'Callback', {@HelpAndAboutCallback, handles, 2});
    
%%  Initilize System

%   Set the default frequency
    InitialSelection = 3;
    set(handles.SelectFrequency, 'value', InitialSelection);
    Frequency = str2num(FrequencyList{InitialSelection});  %#ok<ST2NM>
        
%  Build the initial Code Table
    [CodeTable SampleRate] = FillCodeTable;

%%  HelpAndAboutCallback
    function HelpAndAboutCallback( ~, ~, ~, val )
    %   Displays the help text and the about box
        switch val

            case 1      % Help
                
            %   This opens the help in a pdf viewer
                 % open('MorseCodePlayer.pdf');
                
            %   This opens help in the MatLab browser
                 open('MorseCodePlayer.htm');
                
            %   This opens help in the system browser
                 % path = cd;
                 % str = ['file:///' path '\MorseCodePlayer.htm']
                 % web(str,'-browser');

            case 2      % About
                AboutString = {'Program created by James Willmann',...
                               '           willmann@bellsouth.net',...
                               '          Version 0.5 - 9/27/2009',...
                               '              Using MatLab R2009b'};
                msgbox(AboutString);
        end
    end

%%  UpdateSpeedCallback
    function UpdateSpeedCallback( hObject, ~, handles, val  )
    %	This function handles the code speed and word speed callbacks for
    %	the sliders and the text entries.  The word speed is constraines to
    %	be =< code speed.  The values are displayed and the codeTable is
    %	rebuilt

    %   Get the handles so they are visible here
        handles.SliderWordSpeed = Hand.SliderWordSpeed;
        handles.DisplayWordSpeed = Hand.DisplayWordSpeed;
        handles.DisplayCodeSpeed = Hand.DisplayCodeSpeed;
        handles.SliderCodeSpeed = Hand.SliderCodeSpeed;
    
        switch val       
            case 1      % Code speed text entered
                CodeSpeed = str2double(get(hObject,'string'));
                CodeSpeed = round(10*CodeSpeed)/10;

                if CodeSpeed < MinSpeed
                    CodeSpeed = MinSpeed;
                end
                if CodeSpeed > MaxSpeed
                    CodeSpeed = MaxSpeed;                
                end

                set(handles.SliderCodeSpeed, 'value',...
                    (CodeSpeed - MinSpeed)/(MaxSpeed- MinSpeed));
                set(handles.DisplayCodeSpeed,'string', num2str(CodeSpeed));

                if WordSpeed > CodeSpeed
                    WordSpeed = CodeSpeed;
                    set(handles.SliderWordSpeed,'value',...
                        (CodeSpeed - MinSpeed)/(MaxSpeed- MinSpeed));
                    set(handles.DisplayWordSpeed,'string', num2str(CodeSpeed));
                end

            case 2      % Code speed slider changed
                CodeSpeed = get(hObject,'Value');
                CodeSpeed = (MaxSpeed - MinSpeed)*CodeSpeed + MinSpeed;
                CodeSpeed = round(10*CodeSpeed)/10;
                set(handles.DisplayCodeSpeed,'string', num2str(CodeSpeed));

                if WordSpeed > CodeSpeed
                    WordSpeed = CodeSpeed;
                    set(handles.SliderWordSpeed,'value',...
                        (CodeSpeed - MinSpeed)/(MaxSpeed- MinSpeed));
                    set(handles.DisplayWordSpeed,'string', num2str(CodeSpeed));
                end            

            case 3      % Word speed text entered  
                WordSpeed = str2double(get(hObject,'string'));
                WordSpeed = round(10*WordSpeed)/10;

                if WordSpeed < MinSpeed
                    WordSpeed = MinSpeed;
                end

                if WordSpeed > CodeSpeed
                    WordSpeed = CodeSpeed;
                end

                set(handles.SliderWordSpeed,'value',...
                    (WordSpeed - MinSpeed)/(MaxSpeed- MinSpeed));
                set(handles.DisplayWordSpeed,'string', num2str(WordSpeed));

            case 4      % Word speed slider changed
                WordSpeed = get(hObject,'Value');
                WordSpeed = (MaxSpeed - MinSpeed)*WordSpeed + MinSpeed;
                WordSpeed = round(10*WordSpeed)/10;

                if WordSpeed > CodeSpeed
                    WordSpeed = CodeSpeed;
                    set(handles.SliderWordSpeed,'value',...
                        (WordSpeed - MinSpeed)/(MaxSpeed- MinSpeed));
                end

                set(handles.DisplayWordSpeed,'string', num2str(WordSpeed));            
        end

        %  Update the CodeTable
        [CodeTable SampleRate] = FillCodeTable;

    end

%%  SelectFrequencyCallback
    function SelectFrequencyCallback( hObject, ~, handles )
        %   Gets a new frequency input and rebuilds the CodeTable with
        %   these new values
        
        Selection = get(hObject,'Value');
        if Selection == 1
            Selection = 2;
            set(handles.SelectFrequency, 'value', 2);
        end

        Frequency = str2num(FrequencyList{Selection}); %#ok<ST2NM>

        %  Update the CodeTable

            [CodeTable SampleRate] = FillCodeTable;

    end

%%  XmitAlphabetCallback
    function XmitAlphabetCallback( ~, ~, handles )
    %   Transmits the alphabet (all entries of the CodeTable)
    
        if FileXmitInUse == 1
            return
        end
        
        StopXmit = 0;
        AlphabetInUse = 1;
        
        %  Output the CodeTable a character at a time
        SentString = [];
         for count = 1:59

             if StopXmit == 1
                 AlphabetInUse = 0;
                 return
             end

             SentString = [SentString ' ' CodeTable{count,1}];   
             set(handles.XmitCharacter, 'string', CodeTable{count,1} );
             set(handles.XmitString, 'string', SentString );
             drawnow
             
             wavplay(CodeTable{count,4}, SampleRate);
         end
        AlphabetInUse = 0;
    end

%%  XmitFileCallback
    function XmitFileCallback( ~, ~, handles )
    %   Transmits the file selected by the operator.
    
    %   Make sure its ok to run
        if AlphabetInUse == 1
            return
        end
        
        if  IsFile == 0
            msgbox('You must select a text file first');
            return
        end
        
    %   Initilize
        StopXmit = 0;
        FileXmitInUse = 1;
        ArraySize = size(TextToSend,2);

    %  Output the text string
        SentString = [];
        for n = 1:ArraySize
            Character = TextToSend(n);
            for m=1:60
                if Character == CodeTable{m,1}
                    WaveFile = CodeTable{m,4};
                    break
                end
            end

            if StopXmit == 1
                FileXmitInUse = 0;
                return
            end

            StringMax = 150;
            StringSize = size(SentString,2);
            if StringSize > StringMax
                SentString = SentString(3:StringSize);
            end

            SentString = [SentString ' ' Character];   
            set(handles.XmitCharacter, 'string', char(Character) );
            set(handles.XmitString, 'string', SentString );
            drawnow

            wavplay(WaveFile, SampleRate);
        end
        FileXmitInUse = 0;
    end

%%  XmitKbdCallback
    function XmitKbdCallback( ~, ~, handles ) 
    %	This function initilizes and clears several varables, starts a
    %	timer task ansopens a modak window activating the KeyPressFcn

    %   Dont allow if other modes are running
        if FileXmitInUse ==  1 || AlphabetInUse == 1
            return
        end
        
     %  Set up varables and clear display 
        KbdXmitInUse = 1;
        SentString = [];
        set(handles.XmitCharacter, 'string', ' ' );
        set(handles.XmitString, 'string', SentString );
        drawnow
        
        CharacterCount = 0;         % Use this to count input characters
        SentCount = 0;              % Use this to count output characters
        InputString = cell(2,1);    % Clear input array
        
    %   Start a timer task   
        TimerHandle = timer('TimerFcn',@TimerTaskCallback,...
            'ExecutionMode','fixedSpacing',...
            'Period', .2);
        
   %    Open a modal window to display message and retain focus     
        KbdHandle = figure('KeyPressFcn',{@GetKeyCallback, handles},...
            'WindowStyle', 'modal',...
            'Units', 'pixels',...
            'Toolbar', 'none',...
            'Position',[ XBorder+10, YBorder+10, .3*MaxWindowX, .2*MaxWindowY ],...
            'NumberTitle', 'off',...
            'Name', 'Keyboard Transmission Active ',...
            'MenuBar', 'none',...
            'Resize', 'off',...
            'DockControls', 'off',...
            'Color', White);
        
    %   Text for the modal window
        uicontrol('Style', 'text',...
            'Position', [ .05*MaxWindowX ...
                .01*MaxWindowY ...
                .2*MaxWindowX 5*TextHeight ],...            
            'string', 'Enter text to transmit with the keyboard. Press Escape to terminate Transmission',...
            'BackgroundColor', White,...
            'HorizontalAlignment', 'left',...
            'FontName', 'arial',...
            'FontWeight', 'bold',...
            'FontSize', TextFont ); 
    end

%%  StopXmitCallback
    function StopXmitCallback( ~, ~, ~ ) 
    %   StopXmitCallback stops the timer event and sets the StopXmit flag.
        
        StopXmit = 1;
        
        if KbdXmitInUse > 0
            stop(TimerHandle);
        end

    end

%%  GetTextFileCallback
    function GetTextFileCallback( ~, ~ )
    %GetTextFile - Function lets operator select an input text file, converts
    %it to upper case and removes excess spaces when there are more than one in
    %a row.

    %   Get the file from the operator
        StartDirectory = cd;
        [FileNameWithTag, FileDirectory] = uigetfile({'*.txt'},...
                'Select a Text file',StartDirectory);
        if FileNameWithTag == 0,
            %   If User canceles then display error message
                errordlg('You Must select a File');
            return
        end
        FilePath = [FileDirectory  FileNameWithTag];
        
    %   Load the file
        InputText = fileread(FilePath);
    %   Convert all text to upper
        UpperText = upper(InputText);
    %   Find the space characters in the file
        SpaceData = isspace(UpperText);
    %   Get the length of the file
        InputSize = size(UpperText,2);

        SpaceCount = 0;
        OutputCount = 0;
        for i=1:InputSize
            if SpaceData(1,i) == 0
                OutputCount = OutputCount+1;
                CurrentCharacter = UpperText(1,i);

                if abs(UpperText(1,i)) == 8217
                    CurrentCharacter = '''';
                end

                TextToSend(1,OutputCount) = CurrentCharacter; %#ok<*AGROW>
                SpaceCount = 0;
            else
                if SpaceCount == 0
                    OutputCount = OutputCount+1;
                    TextToSend(1,OutputCount) = ' ';
                    SpaceCount = SpaceCount+1;
                end
            end        
        end
    %   Let them know the file is loaded and ready to send
        IsFile = 1;
    end

%%  FillCodeTable
    function [CodeTable SampleRate] = FillCodeTable
    %Creates the CodeTable and fills it with wave files based on the code and
    %word rate

    %   Get the initial CodeTable
        CodeTable = BuildCodeTable;

    %   Set parameters
            Time = 1/CodeSpeed;
            WordRate = WordSpeed/CodeSpeed;

    %   Make wave samples
        %   Dit File
            Amp = 1;
            [Dit, SampleRate] = MakeWave(Time, Amp, Frequency ); %#ok<*NASGU>
        %   Daw File
            Amp = 1;
            [Daw, SampleRate] = MakeWave(3*Time, Amp, Frequency );
        %   Space
            Amp = 0;
            [Space, SampleRate] = MakeWave(Time, Amp, Frequency );
        %   Character Space
            Amp = 0;
            [CharacterSpace, SampleRate] = MakeWave(3*Time/WordRate, Amp, Frequency );
        %   Word Space
            Amp = 0;
            [WordSpace, SampleRate] = MakeWave(6*Time/WordRate, Amp, Frequency );

     %  Add wave files to the CodeTable array
         for count = 1:59
             CharacterCode = CodeTable{count, 2};
             WaveFile = MakeCharacter( CharacterCode, Dit, Daw, Space, CharacterSpace );
             CodeTable{count,4} = WaveFile;
         end

     %  Add WordSpace at the end
         CodeTable{60,4} = WordSpace;
    end

%%  MakeWave
    function [WavReturn SampleRate ] = MakeWave(Tim, Amp, Freq )
    %   Created a wave file specified by the input parameters.
        NumPerCycle = 200;
        Length = round(Tim*Freq*NumPerCycle);
        SampleRate = Freq*NumPerCycle;
        Sig = zeros(1,Length);
        Co = 2;
        Ro = Co*NumPerCycle;
        Mplier = 1;
        for n = 1:Length
            if n > Length-Ro
                Mplier = (Length-n)/(Length-Ro);
            end
            Sig(n) = Amp*Mplier*sin(2*pi*(n/NumPerCycle));
        end
        WavReturn = Sig ;
    end

%%  MakeCharacter
    function WaveFile = MakeCharacter( CharacterCode, Dit, Daw, Space, CharacterSpace)
    %MakeCharacter puts together the wave file of a complete Morse character

        Siz = size(CharacterCode,2);
    %   Initilize WaveFile
        WaveFile = Space;
    %   Make the WaveFile
        for count = 1:Siz
            Digit = CharacterCode(count);
            switch Digit
                case '.'
                    WaveFile = [WaveFile Dit Space]; %#ok<*AGROW>
                case '-'
                    WaveFile = [WaveFile Daw Space];
                otherwise
                    WaveFile = [WaveFile Space];
            end                
        end
    %   Put a character space at the end
        WaveFile = [WaveFile CharacterSpace];
    end

%%  BuildCodeTable
    function CodeTable = BuildCodeTable
    %   Function to build initial code table
    %   The columns are
    %   1   Character
    %   2   Dot's and dashes
    %   3   dit's and daw's
    %   4   Wave file of character - to be added outside this function

    CodeTable = cell(60,6);

    %   Letters
        CodeTable{1,1} = 'A';
            CodeTable{1,2} = '.-';
            CodeTable{1,3} = 'dit daw';
        CodeTable{2,1} = 'B';
            CodeTable{2,2} = '-...';
            CodeTable{2,3} = 'daw dit dit dit';
        CodeTable{3,1} = 'C';
            CodeTable{3,2} = '-.-.';
            CodeTable{3,3} = 'daw dit daw dit';
        CodeTable{4,1} = 'D';
            CodeTable{4,2} = '-..';
            CodeTable{4,3} = 'daw dit dit';
        CodeTable{5,1} = 'E';
            CodeTable{5,2} = '.';
            CodeTable{5,3} = 'dit';
        CodeTable{6,1} = 'F';
            CodeTable{6,2} = '..-.';
            CodeTable{6,3} = 'dit dit daw dit';
        CodeTable{7,1} = 'G';
            CodeTable{7,2} = '--.';
            CodeTable{7,3} = 'daw daw dit';
        CodeTable{8,1} = 'H';
            CodeTable{8,2} = '....';
            CodeTable{8,3} = 'dit dit dit dit';
        CodeTable{9,1} = 'I';
            CodeTable{9,2} = '..';
            CodeTable{9,3} = 'dit dit';
        CodeTable{10,1} = 'J';
            CodeTable{10,2} = '.---';
            CodeTable{10,3} = 'dit daw daw daw';
        CodeTable{11,1} = 'K';
            CodeTable{11,2} = '-.-';
            CodeTable{11,3} = 'daw dit daw';
        CodeTable{12,1} = 'L';
            CodeTable{12,2} = '.-..';
            CodeTable{12,3} = 'dit daw dit dit';
        CodeTable{13,1} = 'M';
            CodeTable{13,2} = '--';
            CodeTable{13,3} = 'daw daw';
        CodeTable{14,1} = 'N';
            CodeTable{14,2} = '-.';
            CodeTable{14,3} = 'daw dit';
        CodeTable{15,1} = 'O';
            CodeTable{15,2} = '---';
            CodeTable{15,3} = 'daw daw daw';
        CodeTable{16,1} = 'P';
            CodeTable{16,2} = '.--.';
            CodeTable{16,3} = 'dit daw daw dit';
        CodeTable{17,1} = 'Q';
            CodeTable{17,2} = '--.-';
            CodeTable{17,3} = 'daw daw dit daw';
        CodeTable{18,1} = 'R';
            CodeTable{18,2} = '.-.';
            CodeTable{18,3} = 'dit daw dit';
        CodeTable{19,1} = 'S';
            CodeTable{19,2} = '...';
            CodeTable{19,3} = 'dit dit dit';
        CodeTable{20,1} = 'T';
            CodeTable{20,2} = '-';
            CodeTable{20,3} = 'daw';
        CodeTable{21,1} = 'U';
            CodeTable{21,2} = '..-';
            CodeTable{21,3} = 'dit dit daw';
        CodeTable{22,1} = 'V';
            CodeTable{22,2} = '...-';
            CodeTable{22,3} = 'dit dit dit daw';
        CodeTable{23,1} = 'W';
            CodeTable{23,2} = '.--';
            CodeTable{23,3} = 'dit daw daw';
        CodeTable{24,1} = 'X';
            CodeTable{24,2} = '-..-';
            CodeTable{24,3} = 'daw dit dit daw';
        CodeTable{25,1} = 'Y';
            CodeTable{25,2} = '-.--';
            CodeTable{25,3} = 'daw dit daw daw';
        CodeTable{26,1} = 'Z';
            CodeTable{26,2} = '--..';
            CodeTable{26,3} = 'daw daw dit dit';

    %   Numbers
        CodeTable{27,1} = '1';
            CodeTable{27,2} = '.----';
            CodeTable{27,3} = 'dit daw daw daw daw';
        CodeTable{28,1} = '2';
            CodeTable{28,2} = '..---';
            CodeTable{28,3} = 'dit dit daw daw daw';
        CodeTable{29,1} = '3';
            CodeTable{29,2} = '...--';
            CodeTable{29,3} = 'dit dit dit daw daw';
        CodeTable{30,1} = '4';
            CodeTable{30,2} = '....-';
            CodeTable{30,3} = 'dit dit dit dit daw';
        CodeTable{31,1} = '5';
            CodeTable{31,2} = '.....';
            CodeTable{31,3} = 'dit dit dit dit dit';
        CodeTable{32,1} = '6';
            CodeTable{32,2} = '-....';
            CodeTable{32,3} = 'daw dit dit dit dit';
        CodeTable{33,1} = '7';
            CodeTable{33,2} = '--...';
            CodeTable{33,3} = 'daw daw dit dit dit';
        CodeTable{34,1} = '8';
            CodeTable{34,2} = '---..';
            CodeTable{34,3} = 'daw daw daw dit dit';
        CodeTable{35,1} = '9';
            CodeTable{35,2} = '----.';
            CodeTable{35,3} = 'daw daw daw daw dit';
        CodeTable{36,1} = '0';
            CodeTable{36,2} = '-----';
            CodeTable{36,3} = 'daw daw daw daw daw';

    %   Puncuation
        CodeTable{37,1} = ',';
            CodeTable{37,2} = '--..--';
            CodeTable{37,3} = 'daw daw dit dit daw daw';
        CodeTable{38,1} = '.';
            CodeTable{38,2} = '.-.-.-';
            CodeTable{38,3} = 'dit daw dit daw dit daw';
        CodeTable{39,1} = '?';
            CodeTable{39,2} = '..--..';
            CodeTable{39,3} = 'dit dit daw daw dit dit';
        CodeTable{40,1} = ';';
            CodeTable{40,2} = '-.-.-';
            CodeTable{40,3} = 'daw dit daw dit daw';
        CodeTable{41,1} = ':';
            CodeTable{41,2} = '---...';
            CodeTable{41,3} = 'daw daw daw dit dit dit';
        CodeTable{42,1} = '/';
            CodeTable{42,2} = '-..-.';
            CodeTable{42,3} = 'daw dit dit daw dit';
        CodeTable{43,1} = '=';
            CodeTable{43,2} = '-...-';
            CodeTable{43,3} = 'daw dit dit dit daw';
        CodeTable{44,1} = '''';
            CodeTable{44,2} = '.----.';
            CodeTable{44,3} = 'dit daw daw daw daw dit';
        CodeTable{45,1} = '(';
            CodeTable{45,2} = '-.--.';
            CodeTable{45,3} = 'daw dit daw daw dit';
        CodeTable{46,1} = ')';
            CodeTable{46,2} = '-.--.-';
            CodeTable{46,3} = 'daw dit daw daw dit daw';
        CodeTable{47,1} = '-';
            CodeTable{47,2} = '-....-';
            CodeTable{47,3} = 'daw dit dit dit dit daw';
        CodeTable{48,1} = '+';
            CodeTable{48,2} = '.-.-.';
            CodeTable{48,3} = 'dit daw dit daw dit';
        CodeTable{49,1} = '@';
            CodeTable{49,2} = '.--.-.';
            CodeTable{49,3} = 'dit daw daw dit daw dit';
        CodeTable{50,1} = '"';
            CodeTable{50,2} = '.-..-';
            CodeTable{50,3} = 'dit daw dit dit daw dit';
        CodeTable{51,1} = '!';
            CodeTable{51,2} = '-.-.--';
            CodeTable{51,3} = 'daw dit daw dit daw daw';
        CodeTable{52,1} = '$';
            CodeTable{52,2} = '...-..-';
            CodeTable{52,3} = 'dit dit dit daw dit dit daw';
        CodeTable{53,1} = '&';
            CodeTable{53,2} = '.-...';
            CodeTable{53,3} = 'dit daw dit dit dit';

    %   Special Alphabet Characters
        CodeTable{54,1} = '';
            CodeTable{54,2} = '.--.-';
            CodeTable{54,3} = 'dit daw daw dit daw';
        CodeTable{55,1} = '';
            CodeTable{55,2} = '.-.-';
            CodeTable{55,3} = 'dit daw dit daw';
        CodeTable{56,1} = '';
            CodeTable{56,2} = '..-..';
            CodeTable{56,3} = 'dit dit daw dit dit';
        CodeTable{57,1} = '';
            CodeTable{57,2} = '--.--';
            CodeTable{57,3} = 'daw daw dit daw daw';
        CodeTable{58,1} = '';
            CodeTable{58,2} = '---.';
            CodeTable{58,3} = 'daw daw daw dit';
        CodeTable{59,1} = '';
            CodeTable{59,2} = '..--';
            CodeTable{59,3} = 'dit dit daw daw';

        %   Word space
        CodeTable{60,1} = ' ';
            CodeTable{60,2} = '   ';
            CodeTable{60,3} = '   ';
    end

%%  DisplayCodeTableCallback
    function DisplayCodeTableCallback( ~, ~, handles )
    %   This function takes the CodeTable and displays it in a
    %   table on a new figure
 
    %   Create the new figure
        handles.TableFigHandle = figure(...
            'Units', 'pixels',...
            'Position',[ XBorder, YBorder, MaxWindowX/2, 2*MaxWindowY/3 ],...
            'NumberTitle', 'off',...
            'Toolbar', 'none',...
            'Name', 'Morse Code Table',...
            'MenuBar', 'none',...
            'Resize', 'off',...
            'DockControls', 'off',...
            'Color', White);

    %   Build the data cell array to display
        DisplayData = cell(60,3);
        for i = 1:60
            for j = 1:3
                DisplayData(i,j) = CodeTable(i,j);
                if j==1
                DisplayData{i,j} = ['      ' DisplayData{i,j}];
                end
                if j==2
                DisplayData{i,j} = ['         ' DisplayData{i,j}];
                end
                if j==3
                DisplayData{i,j} = ['   ' DisplayData{i,j}];
                end
            end
        end
        

    %   Build the table
        TextFont = round(3*MaxMonitorY/200)-2;
        Offset = 22;        
        ColumnNames = {'Character' 'Dots and Dashes' 'Dits and Daws'};
        Width = (MaxWindowX)/6-Offset/3-3;
        Share = Width/2;
        ColumnWidths = {Width-Share Width Width+Share};

        handles.DataArray = uitable('ColumnName', ColumnNames,...
            'ColumnWidth', ColumnWidths,...
            'RowName', [],...
            'Data', DisplayData,...
            'FontSize', TextFont,...
            'Position', [1 1 ...
                MaxWindowX/2 2*MaxWindowY/3]);
    
    end

%%  GetKeyCallback
    function GetKeyCallback( ~, evnt, ~ )
    %   This function gets each key event from the keyboard and builds it
    %   into a 2xn cell array InputString along with its respective Morse
    %   wave file.  This continues until an easape key event is detected at
    %   which time the process is terminated.
    
    %   Close and get out if escape is entered
        if strcmp(evnt.Key, 'escape')
            close(KbdHandle);
            KbdHandle = 0;
            InputString{1,CharacterCount+1} = 'end';
            return
        end
        
    %   Remove the last character if backspace is entered    
        if strcmp(evnt.Key, 'backspace')
            CharacterCount = CharacterCount-1;
            InputString = InputString(:,1:CharacterCount);
            return
        end
        
     %  Get the character, convert to upper and find it in the CodeTable   
        Character = upper(evnt.Character);
        Found = 0;

        for m=1:60
            if Character == CodeTable{m,1}
                CharacterCount = CharacterCount+1;
                InputString{1,CharacterCount} = Character;
                InputString{2,CharacterCount} = CodeTable{m,4};
                Found = 1;
                break
            end
        end
        
     %  If it wasnt found get out and wait for the next one   
        if Found == 0;
            return
        end
        
    %   Start the timer when the first valid character has been entered    
        if CharacterCount == 1
            start(TimerHandle);
        end
        
    end 

%%  TimerTaskCallback
    function TimerTaskCallback(~,~)
    %   This task is entered by the timer every .2 seconds to see if a new 
    %   character has been added to InputString. If so it is transmitted 
    %   and the SentCount is increased. 
        
        SizeIn = size(InputString,2);
        
        if SizeIn <= SentCount
            return
        end
        
        SentCount = SentCount+1;
        Character = InputString{1,SentCount};
        
        if strcmp(Character,'end')
            stop(TimerHandle);
            KbdXmitInUse = 0;
            return
        end
        
        WaveFile = InputString{2,SentCount};
        
        StringMax = 150;
        StringSize = size(SentString,2);
        if StringSize > StringMax
            SentString = SentString(3:StringSize);
        end

        SentString = [SentString ' ' Character];   
        set(handles.XmitCharacter, 'string', char(Character) );
        set(handles.XmitString, 'string', SentString );
        drawnow

        wavplay(WaveFile, SampleRate);
                  
    end

end

Contact us