PRC163Radio class

L3Harris PRC-163 radio adapter.

This device reads and write asynchronously from a USB TTY device connected to the radio. It has a thread managing the low-level IO, and a timer to periodically send the commands that turn into the events required by the BDE (GPS, time, etc.).

Both reading and writing happen in two stages: a queue and a buffer.

write_queue -> write_bufffer -> <TTY> -> read_buffer -> read_queue

Commands for the radio are appended to the write_queue. The IO thread pulls each command off of the queue and appends it to the write_buffer, then writes as much as it can to the radio. It then reads any pending output from the radio into the read_buffer and then breaks as many whole lines off of that as it can, appending each one to the read_queue. The read_queue is then scanned for events, and those are emitted up the tree.

== Emits:

  • sys.device.info

    • sys.gps.position

    • sys.gps.time

    • sys.radio.battery

    • sys.radio.channel

    • sys.radio.volume

    • sys.radio.versions

== Consumes:

  • sys.radio.command

This class includes code from the ruby-termios examples, used under the terms of the Ruby License. The original software does not include a copyright statement, so none is duplicated here.

Refs:

  • L3Harris Ascii Interface Design Document (RF-335M/1.3.1/February 19, 2021) obsidian://open?vault=Software%20Notes&file=Radios%2FL3Harris%20PRC-163%2Fattachments%2FL3Harris%20Ascii%20PLATFORM%20163%201.3.1.pdf

Constants

AUTOVIVIFY

Auto-vivifying Hash Proc; used to create Hashes that auto-expand their contents

COMMAND_PROMPT

Prompt text that is used to split up output

IGNORED_LINE

Lines that are ignored while parsing multi-line command output

IO_SELECT_TIMEOUT

Maximum number of floating-point seconds to wait in the IO loop. This is effectively the maximum amount of time a command will spend in the write_queue before being sent.

L3H_DMS_FORMAT

Pattern for matching L3H’s weird degrees/minutes/seconds format.

LINE_SEPARATOR

Character/s used to split radio output into lines

MAX_READ_SIZE

Maximum of bytes to read from the radio at one time

OOB_MESSAGE_LINE

Pattern to match ongoing (out-of-band) status message lines in radio output

PRESET_CHANGE_COMPLETE

Pattern that matches the OOB message when a preset is changed

RESET_THROTTLE_TIME

How long to pause when resetting to prevent the device from spinning

Attributes

io_thread R

The Thread object that is reading and writing to/from the radio serial device

periodic_command_timer R

Concurrent::TimerTask that causes periodic information events to be emitted from the device.

read_buffer R

A String buffer that contains output that has been read from the radio but not yet split into lines into the read_queue

read_queue R

Line-oriented output from the radio that is waiting to be parsed

write_buffer R

A String with unwritten commands for the radio. Commands are appended to this as they’re removed from the write_queue by the IO thread.

write_queue R

Commands for the radio that haven’t been written yet.

Public Class Methods

new( * )

Create a new device adapter for a L3H PRC-163 radio.

# File lib/ravn/hal/device/prc163_radio.rb, line 152
def initialize( * )
        @radio        = nil
        @io_thread    = nil

        @read_queue   = []
        @read_buffer  = String.new

        @write_queue  = []
        @write_buffer = String.new

        @periodic_command_timer = self.make_periodic_command_timer or
                raise "couldn't create the periodic read timer"

        if self.class.simulation_mode?
                self.log.warn "Adding simulation mode to %p" % [ self ]
                self.extend( SimulationMode )
        end

        super
end

Public Instance Methods

buffer_pending_writes()

Append any data in the write_queue to the write_buffer.

# File lib/ravn/hal/device/prc163_radio.rb, line 325
def buffer_pending_writes
        while (data = self.write_queue.shift)
                self.write_buffer << data << LINE_SEPARATOR
        end
end
build_gps_message( gps_info )

Construct and return a ‘sys.gps.position’ message using the given gps_info.

# File lib/ravn/hal/device/prc163_radio.rb, line 523
def build_gps_message( gps_info )
        return unless gps_info[ :state ] == :tracking

        gps = {
                pos: [ gps_info[ :latitude ], gps_info[ :longitude ] ],
                hae: gps_info[ :altitude ],
                ce:  gps_info[ :epe ],
                heading: gps_info[ :heading ],
                velocity: gps_info[ :velocity ]
        }

        return Ravn::HAL::Message.new( 'sys.gps.position', data: gps )
end
build_gps_time_message( gps_info )

Construct and return a ‘sys.gps.time’ message using the given gps_info.

# File lib/ravn/hal/device/prc163_radio.rb, line 539
def build_gps_time_message( gps_info )
        time = gps_info[ :timestamp ].to_i
        return Ravn::HAL::Message.new( 'sys.gps.time', data: { time: time } )
end
build_radio_connection()

Connect to the radio via serial UART.

# File lib/ravn/hal/device/prc163_radio.rb, line 388
def build_radio_connection
        device = self.class.serial_device
        speed = self.class.serial_speed
        mode = self.class.serial_mode

        self.log.info "Opening radio device: %s at %d(%s)" % [ device, speed, mode ]
        uart = UART.open( device.to_s, speed, mode )

        desc = self.dump_termios( uart )
        self.log.info "Radio device terminal capabilities: %s" % [ desc ]

        return uart
rescue SystemCallError => err
        self.log.error "%p while opening the serial device: %s" % [ err.class, err.message ]
        raise
end
emit_preset_change_complete_event( match_data )

Emit a sys.radio.channel event given match_data for a OOB SYS_PRESETSTATUS line.

# File lib/ravn/hal/device/prc163_radio.rb, line 558
def emit_preset_change_complete_event( match_data )
        transceiver = match_data[:transceiver][ /(\d+)/, 1 ] or
                raise "no transceiver in match data: %p" % [ match_data ]
        data = {
                radio: self.class.object_id,
                transceiver: transceiver.to_i,
                name: match_data[:preset_name],
                number: match_data[:preset_number].to_i,
                waveform: match_data[:waveform],
        }
        message = Ravn::HAL::Message.new( 'sys.radio.channel', data: )
        self.filter_up( message )
end
gather_device_info()

Return a Hash that contains information describing this device for intra-device communication; overridden to set the correct vendor.

# File lib/ravn/hal/device/prc163_radio.rb, line 223
def gather_device_info
        return super.merge( vendor: 'l3harris' )
end
handle_oob_status_line( line )

Handle any status lines emitted by the radio by emitting events for the stuff we care about.

# File lib/ravn/hal/device/prc163_radio.rb, line 547
def handle_oob_status_line( line )
        case line
        when PRESET_CHANGE_COMPLETE
                self.emit_preset_change_complete_event( $~ )
        else
                self.log.debug "Unhandled OOB status line: %p" % [ line ]
        end
end
handle_paused_event( * )

Stop reading/writing timers while paused

# File lib/ravn/hal/device/prc163_radio.rb, line 229
def handle_paused_event( * )
        self.periodic_command_timer&.shutdown
        @periodic_command_timer = self.make_periodic_command_timer
end
handle_resetting_event( * )

Tear down the radio connection and wait for the IO thread to stop when the device is going to be reset.

# File lib/ravn/hal/device/prc163_radio.rb, line 237
def handle_resetting_event( * )
        self.log.warn "Resetting the radio device and waiting on the IO thread."
        @radio&.close
        @radio = nil

        self.io_thread&.join( RESET_THROTTLE_TIME )
end
handle_resumed_event( * )

Recreate and restart the reading/writing timers when resumed

# File lib/ravn/hal/device/prc163_radio.rb, line 247
def handle_resumed_event( * )
        self.periodic_command_timer.execute
end
io_loop()

Connect to the radio and loop over the IO routine for it while it’s open.

# File lib/ravn/hal/device/prc163_radio.rb, line 279
def io_loop
        Thread.current.report_on_exception = true

        self.log.info "Starting IO loop."
        while self.radio && ! self.radio.closed?
                self.read_and_write
        end
        self.log.info "Stopped IO loop."
rescue SystemCallError => err
        self.log.error( err )
        reason = "%p in the IO loop: %s" % [ err.class, err.message ]
        self.reset( reason )
end
parse_and_emit_events()

Turn data in the read_queue into events if possible.

# File lib/ravn/hal/device/prc163_radio.rb, line 574
def parse_and_emit_events
        self.log.debug "Parsing and emitting events from the read queue..."
        chunks = self.read_queue.slice_after( COMMAND_PROMPT )

        self.log.debug "Chunks: %p" % [ chunks.to_a ]

        chunks.each do |chunk|
                unless chunk.last.match?( COMMAND_PROMPT )
                        self.log.debug "No command prompt in the last line of this chunk: %p" % [ chunk.last ]
                        break
                end

                self.log.debug "Looking for events in chunk: %p" % [ chunk ]
                self.parse_event_from_chunk( chunk ) or break
                self.log.debug "Successfully parsed chunk; dropping %d lines from the queue" % [ chunk.size ]
                self.read_queue.drop( chunk.size )
        end
rescue => err
        self.log.error "%p while parsing for events: %s" % [ err.class, err.full_message ]
        raise
end
parse_event_from_chunk( lines )

Extract data from the given chunk of input lines and emit an event for it if it’s interesting.

# File lib/ravn/hal/device/prc163_radio.rb, line 599
def parse_event_from_chunk( lines )
        case lines.first
        when /^VERSION /i
                self.parse_into_versions_event( lines )
        when /^BATTERY\b/i
                self.parse_into_battery_event( lines )
        when /^GPS\b/i
                self.parse_into_gps_event( lines )
        when /^RT[12] STATE\b/i
                self.parse_into_channel_event( lines )
        when /^RT[12] VOLUME\b/i
                self.parse_into_volume_event( lines )
        else
                self.log.debug "Unhandled multi-line event: %s" % [ lines.join("⏎") ]
        end
end
parse_into_battery_event( lines )

Parse the given BATTERY output lines and emit an event describing the battery status.

# File lib/ravn/hal/device/prc163_radio.rb, line 728
def parse_into_battery_event( lines )
        radio = self.class.object_id
        data = lines.each_with_object({ radio: }) do |line, hash|
                case line
                when IGNORED_LINE
                        next

                when /^BATTERY HUB BOD (.*)$/
                        # Sun Dec  8 15:51:08 2019
                        timestamp = Time.strptime( $1.strip, '%a %b %e %H:%M:%S %Y' )
                        hash[:hub] ||= {}
                        hash[:hub][:bod] = timestamp
                when /^BATTERY HUB CAPACITY DAYS REMAINING (\d+)/
                        hash[:hub] ||= {}
                        hash[:hub][:capacity] = $1.to_i
                when /^BATTERY TYPE (\w+)/
                        hash[:type] = $1
                when /^BATTERY MODEL (\w+)/
                        hash[:model] = $1
                when /^BATTERY CAPACITY (\w+)/
                        hash[:capacity] = $1
                when /^BATTERY STATUS (\w+)/
                        hash[:status] = $1
                when /^BATTERY CHARGE (\d+)/
                        hash[:charge] = $1.to_i
                when /^BATTERY VOLTAGE (\d+)/
                        hash[:voltage] = $1.to_i
                when /^BATTERY CURRENT (\d+)/
                        hash[:current] = $1.to_i
                when /^BATTERY TEMP (\d+)/
                        hash[:temp] = $1.to_i

                else
                        self.log.debug "Unhandled battery info line: %p" % [ line ]
                end
        end

        message = Ravn::HAL::Message.new( 'sys.radio.battery', data: )
        self.filter_up( message )
end
parse_into_channel_event( lines )

Parse the given RT STATE output lines and emit an event describing the transceiver’s channel state.

# File lib/ravn/hal/device/prc163_radio.rb, line 650
def parse_into_channel_event( lines )
        radio = self.class.object_id

        transceiver = lines.first[ /^RT([12])/i, 1 ] or
                raise "failed to match transceiver number from %p" % [ lines.first ]
        transceiver = transceiver.to_i

        data = lines.each_with_object({ radio:, transceiver: }) do |line, hash|
                case line
                when IGNORED_LINE
                        next

                when /^RT[12] STATE OPMODE (\S+)$/
                        hash[:opmode] = $1.downcase.to_sym
                when /^RT[12] STATE SYS_PRESET_NAME (.+)$/
                        hash[:name] = $1.strip
                when /^RT[12] STATE SYS_PRESET_NUMBER\s+(\d+)/
                        hash[:number] = $1.to_i
                when /^RT[12] STATE CURRENT_WAVEFORM (.+)$/
                        hash[:waveform] = $1.strip
                when /^RT[12] STATE OPERATIONAL_STATE (\w+)/
                        hash[:state] = $1.downcase.to_sym
                when /^RT[12] STATE MISSION_PLAN (\S+)/
                        hash[:plan] = $1
                when /^RT[12] STATE TYPE1-I (\S+)/
                        hash[:encrypted] = ($1 == 'TRUE')

                else
                        self.log.debug "Unhandled transceiver state line: %p" % [ line ]
                end
        end

        message = Ravn::HAL::Message.new( 'sys.radio.channel', data: )
        self.filter_up( message )
end
parse_into_gps_event( lines )

Read GPS info from the given GPS output lines emit an event describing the current GPS position.

# File lib/ravn/hal/device/prc163_radio.rb, line 772
def parse_into_gps_event( lines )
        info = lines.each_with_object({}) do |line, hash|
                case line
                when IGNORED_LINE
                        # ignore/no-op
                when /^GPS INFO\s*$/
                        # ignore
                when /^GPS INFO POS1 (.*)/
                        hash[ :pos1 ] = $1
                when /^GPS INFO POS2 (.*)/
                        hash[ :pos2 ] = $1
                when /^GPS INFO STATE (\w+)/
                        hash[ :state ] = $1.downcase.to_sym
                when /^GPS INFO LATITUDE\s+(.+)/
                        hash[ :latitude ] = parse_dms_position( $1 )
                when /^GPS INFO LONGITUDE\s+(.+)/
                        hash[ :longitude ] = parse_dms_position( $1 )
                when /^GPS INFO ALTITUDE\s+(.+)/
                        hash[ :altitude ] = $1.to_f
                when /^GPS INFO SEPARATION\s+(.+)/
                        hash[ :separation ] = $1.to_f
                when /^GPS INFO HEADING\s+(.+)/
                        hash[ :heading ] = $1.to_f
                when /^GPS INFO VELOCITY\s+(.+)/
                        hash[ :velocity ] = $1.to_f
                when /^GPS INFO FOM\s+(.+)/
                        hash[ :fom ] = $1.to_i
                when /^GPS INFO TFOM\s+(.+)/
                        hash[ :tfom ] = $1.to_i
                when /^GPS INFO EPE\s+\+\/-\s+(.+?) m/
                        hash[ :epe ] = $1.to_f
                when /^GPS INFO DATUM\s+(.+)/
                        hash[ :datum ] = $1.downcase.to_sym
                when /^GPS INFO KEYSTATUS\s+(.+)/
                        hash[ :keystatus ] = $1.downcase.to_sym
                when /^GPS INFO VALIDITY\s+(.+)/
                        hash[ :validity ] = $1.downcase.to_sym
                when /^GPS INFO TIMESTAMP\s+(.+)/
                        begin
                                munged_time = $1 + ' UTC'
                                time = Time.strptime( munged_time, '%Y-%m-%d %H:%M:%S %Z' )
                                hash[ :timestamp ] = time
                        rescue ArgumentError => err
                                self.log.warn "%p while parsing GPS timestamp: %s" % [ err.class, err.message ]
                        end
                else
                        self.log.debug "Unknown GPS INFO line: %p" % [ line ]
                end
        end

        if (message = self.build_gps_message( info ))
                self.filter_up( message )
        else
                self.log.warn "couldn't create GPS position message (GPS state = %p)" % [ info[:state] ]
        end

        if (message = self.build_gps_time_message( info ))
                self.filter_up( message )
        else
                self.log.warn "couldn't create GPS time message (GPS state = %p)" % [ info[:state] ]
        end
end
parse_into_versions_event( lines )

Parse the given VERSION output lines and emit an event describing various versions of hardware and software running on the radio.

# File lib/ravn/hal/device/prc163_radio.rb, line 689
def parse_into_versions_event( lines )
        data = Hash.new( &AUTOVIVIFY )
        data[ :radio ] = self.class.object_id

        lines.each do |line|
                case line
                when IGNORED_LINE
                        next
                when /^VERSION ALL/
                        # Ignored for now
                        next
                when %r{^VERSION OPTION NAME (?<name>\w+)\s+P/N (?<partno>\S+)\s+(?<desc>.+)$}
                        hashify_captures_into( $~, data[:options] )

                when /^VERSION HW (?<name>\w+)\s+(?<sku>\w+)\s+PN (?<partno>\S+)\s+PL_REV (?<plrev>\w+)\s+PWB_REV (?<pwbrev>\w+)/
                        hashify_captures_into( $~, data[:hw] )

                when %r{^VERSION INFOSEC (?<name>\p{Print}+)\s+P/N (?<partno>\S+)\s+REVISION (?<revision>\S+)}
                        hashify_captures_into( $~, data[:infosec] )

                # VERSION SW GPS_GRAM_SAASM P/N N/A REVISION 811-7994-005 SW_REV N/A
                when %r{^VERSION SW (?<name>\S+)\s+P/N (?<partno>\S+)\s+REVISION (?<revision>.+?)\s+SW_REV (\S+)}
                        hashify_captures_into( $~, data[:sw] )

                when /^VERSION SYSTEM SW\s+REVISION (\S+ \S+)/
                        data[:system] = $1

                else
                        self.log.debug "Unhandled software version line: %p" % [ line ]
                end
        end

        message = Ravn::HAL::Message.new( 'sys.radio.versions', data: )
        self.filter_up( message )
end
parse_into_volume_event( lines )

Parse the given RT VOLUME output lines and emit an event describing the transceiver’s volume level.

# File lib/ravn/hal/device/prc163_radio.rb, line 619
def parse_into_volume_event( lines )
        radio = self.class.object_id

        transceiver = lines.first[ /^RT([12])/i, 1 ] or
                raise "failed to match transceiver number from %p" % [ lines.first ]
        transceiver = transceiver.to_i

        data = lines.each_with_object({ radio:, transceiver: }) do |line, hash|
                case line
                when IGNORED_LINE
                        next
                when /^RT[12] VOLUME (\d+|MIN|MAX)/
                        level = case $1
                                when 'MIN' then 0
                                when 'MAX' then 10
                                else
                                        $1.to_i
                                end
                        hash[:level] = level
                else
                        self.log.debug "Unhandled transceiver state line: %p" % [ line ]
                end
        end

        message = Ravn::HAL::Message.new( 'sys.radio.volume', data: )
        self.filter_up( message )
end
queue_command( *commands )
Alias for: queue_commands
queue_commands( *commands )

Add new commands to the write queue.

# File lib/ravn/hal/device/prc163_radio.rb, line 411
def queue_commands( *commands )
        self.write_queue.push( *commands )
end
Also aliased as: queue_command
queue_periodic_commands( * )

Timer callback: queue commands for events that should be emitted periodically.

# File lib/ravn/hal/device/prc163_radio.rb, line 419
def queue_periodic_commands( * )
        self.request_gps_info
        self.request_rt_info
end
queue_read_lines()

Split off complete lines from the read_buffer and append then to the read queue.

# File lib/ravn/hal/device/prc163_radio.rb, line 344
def queue_read_lines
        self.log.debug "Queueing read lines..."
        if (index = self.read_buffer.rindex( COMMAND_PROMPT ))
                index += 6 # Length of command prompt
                self.log.debug "  reading lines up to index %d" % [ index ]

                if (line_data = self.read_buffer.slice!( 0, index ))
                        self.log.debug "  read: %p" % [ line_data ]

                        line_data.each_line do |line|
                                line.chomp!

                                if OOB_MESSAGE_LINE.match?( line )
                                        self.handle_oob_status_line( line )
                                else
                                        self.read_queue.push( line )
                                end
                        end
                        self.log.debug "  there are now %d queued lines" % [ self.read_queue.length ]
                else
                        self.log.warn "no line data! (0..%d of %p)" % [ index, self.read_buffer ]
                end
        else
                self.log.debug "No complete lines in %d bytes of data; skipping." %
                        [ self.read_buffer.bytesize ]
        end
end
radio()

Fetch the radio device, creating it if it wasn’t already.

# File lib/ravn/hal/device/prc163_radio.rb, line 265
def radio
        return @radio ||= self.build_radio_connection
end
read_and_write()

Wait on the radio to be readable and/or writable and handle reading and writing when it’s ready.

# File lib/ravn/hal/device/prc163_radio.rb, line 296
def read_and_write
        radio_dev = self.radio or raise "Couldn't connect to the radio"

        ios = [ radio_dev ]
        readable = ios

        self.buffer_pending_writes
        writable = self.write_buffer.empty? ? nil : ios

        if (ready = IO.select( readable, writable, nil, IO_SELECT_TIMEOUT ))

                # Readable
                if ready[ 0 ]
                        self.read_from_radio
                        self.queue_read_lines
                        self.parse_and_emit_events
                end

                # Writable
                if ready[ 1 ]
                        self.write_to_radio
                end
        end
rescue IO::WaitReadable, IO::WaitWritable => err
        self.log.debug "%p while reading/writing to the radio: %s" % [ err.class, err.message ]
end
read_from_radio()

Read output from the radio and generate events from it. It propagates errors from the read to its caller, notably IO::WaitReadable exceptions and EOFError.

# File lib/ravn/hal/device/prc163_radio.rb, line 334
def read_from_radio
        if (data = self.radio&.read_nonblock( MAX_READ_SIZE ))
                self.log.debug "Read %d bytes from the radio" % [ data.bytesize ]
                self.read_buffer << data
        end
end
request_battery_info()

Queue up the command to emit battery info events.

# File lib/ravn/hal/device/prc163_radio.rb, line 438
def request_battery_info
        self.queue_command( 'BATTERY' )
end
request_gps_info()

Queue up the command to emit GPS info events.

# File lib/ravn/hal/device/prc163_radio.rb, line 432
def request_gps_info
        self.queue_command( 'GPS INFO' )
end
request_next_preset( transceiver )

Queue up the command to change the given transceiver‘s preset to the next one.

# File lib/ravn/hal/device/prc163_radio.rb, line 491
def request_next_preset( transceiver )
        command = "RT%d SYS_PRESET SELECT NEXT" % [ transceiver ]
        self.queue_command( command )
end
request_previous_preset( transceiver )

Queue up the command to change the given transceiver‘s preset to the previous one.

# File lib/ravn/hal/device/prc163_radio.rb, line 484
def request_previous_preset( transceiver )
        command = "RT%d SYS_PRESET SELECT PREVIOUS" % [ transceiver ]
        self.queue_command( command )
end
request_rt_change( message )
# File lib/ravn/hal/device/prc163_radio.rb, line 455
def request_rt_change( message )
        target_id = message.data[ :radio ]
        unless target_id == self.identifier #BR/not equal/nil
                self.log.debug "Ignoring `command` event for radio %p" % [ target_id ]
                return
        end

        transceiver = message.data[ :transceiver ]
        unless transceiver&.between?( 1, 2 ) #BR/less than/greater than
                self.log.error "Invalid/unset transceiver (%p) for message %p" % [ transceiver, message ]
                return
        end

        case message.data[ :command ] #BR/else/nil
        when 'channel up'
                self.request_next_preset( transceiver )
        when 'channel down'
                self.request_previous_preset( transceiver )
        when 'volume set'
                self.request_volume_set( transceiver, message )
        when nil
                self.log.warn "No `command` specified for %p" % [ message ]
        else
                self.log.warn "Command %p not implemented." % [ message.data[:command] ]
        end
end
request_rt_info()

Queue commands that will emit events describing the status of the radio transmitters (preset, volume, etc.).

# File lib/ravn/hal/device/prc163_radio.rb, line 445
def request_rt_info
        self.queue_command( "RT1 STATE ALL" )
        self.queue_command( "RT1 VOLUME" )
        self.queue_command( "RT2 STATE ALL" )
        self.queue_command( "RT2 VOLUME" )
end
request_version_info()

Queue up the command to emit version info events.

# File lib/ravn/hal/device/prc163_radio.rb, line 426
def request_version_info
        self.queue_command( 'VERSION ALL' )
end
request_volume_set( transceiver, message )

Queue up the command to change the given transceiver‘s volume to the level specified by the message.

# File lib/ravn/hal/device/prc163_radio.rb, line 499
def request_volume_set( transceiver, message )
        if (level = message.data[ :level ])
                unless level.between?( 0, 12 )
                        self.log.warn "Ignoring request to set volume to %p (0 <= level <=12)" %
                                [ level ]
                        return
                end
        else
                self.log.error "No `level` given for `volume set` command: %p" % [ message ]
                return
        end

        command = "RT%d VOLUME %d" % [ transceiver, level ]
        self.queue_command( command )
end
reset( reason )

Reset the device.

# File lib/ravn/hal/device/prc163_radio.rb, line 253
def reset( reason )
        self.log.error( reason )
        sleep( RESET_THROTTLE_TIME )
        self.core&.parent&.tell( reset_device: self )
end
start()

Fire up an interval timer for radio messages.

# File lib/ravn/hal/device/prc163_radio.rb, line 211
def start
        super

        self.periodic_command_timer.execute
        self.request_version_info

        @io_thread = self.start_io_loop unless self.class.simulation_mode?
end
start_io_loop()

Start reading and writing from/to the radio. Emits events for information read, and writes commands from the write_queue if there are any. Returns a Thread that is running the loop.

# File lib/ravn/hal/device/prc163_radio.rb, line 273
def start_io_loop
        return Thread.new( &self.method(:io_loop) )
end
write_to_radio()

Write any pending commands to the radio.

# File lib/ravn/hal/device/prc163_radio.rb, line 374
def write_to_radio
        return if self.write_buffer.empty?

        self.log.debug "Trying to write %d bytes to the radio" % [ self.write_buffer.bytesize ]
        bytes = self.radio&.write_nonblock( self.write_buffer )

        if bytes&.nonzero?
                self.log.debug "wrote %d bytes to the radio" % [ bytes ]
                self.write_buffer.slice!( 0, bytes )
        end
end

Protected Instance Methods

make_periodic_command_timer()

Build a Concurrent::TimerTask that will read output from the radio and generate events from it.

# File lib/ravn/hal/device/prc163_radio.rb, line 842
def make_periodic_command_timer
        timer = Concurrent::TimerTask.new { self.queue_periodic_commands }
        timer.execution_interval = self.class.status_frequency
        timer.add_observer( Ravn::LoggingTaskObserver.new(:prc163_periodic_command_timer) )

        return timer
end