lib/aixm/f.rb

Summary

Maintainability
A
0 mins
Test Coverage
using AIXM::Refinements

module AIXM

  # Radio frequency for communication, navigation and so forth.
  #
  # @example
  #   AIXM.f(123.35, :mhz)
  class F
    include AIXM::Concerns::HashEquality
    extend Forwardable

    UNITS = %i(ghz mhz khz).freeze

    # @!method zero?
    #   @return [Boolean] whether frequency is zero
    def_delegator :@freq, :zero?

    # Frequency
    #
    # @overload freq
    #   @return [Float]
    # @overload freq=(value)
    #   @param value [Float]
    attr_reader :freq

    # Unit
    #
    # @overload unit
    #   @return [Symbol] any of {UNITS}
    # @overload unit=(value)
    #   @param value [Symbol] any of {UNITS}
    attr_reader :unit

    # See the {overview}[AIXM::F] for examples.
    def initialize(freq, unit)
      self.freq, self.unit = freq, unit
    end

    # @return [String]
    def inspect
      %Q(#<#{self.class} #{to_s}>)
    end

    # @return [String] human readable representation (e.g. "123.35 mhz")
    def to_s
      [freq, unit].join(' '.freeze)
    end

    def freq=(value)
      fail(ArgumentError, "invalid freq") unless value.is_a? Numeric
      @freq = value.to_f
    end

    def unit=(value)
      fail(ArgumentError, "invalid unit") unless value.respond_to? :to_sym
      @unit = value.to_sym.downcase
      fail(ArgumentError, "invalid unit") unless UNITS.include? @unit
    end

    # Whether this frequency is part of a frequency band.
    #
    # @return [Boolean]
    def between?(lower_freq, upper_freq, unit)
      freq.between?(lower_freq, upper_freq) && self.unit == unit
    end

    # @see Object#==
    def ==(other)
      self.class === other && freq == other.freq && unit == other.unit
    end

    # Whether this frequency is part of the voice airband for civil aviation
    # using +AIXM.config.voice_channel_separation+.
    #
    # @return [Boolean]
    def voice?
      return false unless unit == :mhz
      case AIXM.config.voice_channel_separation
        when 25 then voice_25?
        when 833 then voice_833?
        when :any then voice_25? || voice_833?
        else fail(ArgumentError, "unknown voice channel separation")
      end
    end

    # Whether this frequency is for emergencies only.
    #
    # @return [Boolean]
    def voice_emergency?
      self == AIXM::EMERGENCY
    end

    private

    # Whether this frequency is part of the voice airband for civil aviation
    # using 25 kHz channel separation.
    #
    # @return [Boolean]
    def voice_25?
      return false unless unit == :mhz && freq == freq.round(3) && freq.between?(118, 136.975)
      ((freq * 1000).round % 25).zero?
    end

    # Whether this frequency is part of the voice airband for civil aviation
    # using 8.33 kHz channel separation.
    #
    # @return [Boolean]
    def voice_833?
      return false unless unit == :mhz && freq == freq.round(3) && freq.between?(118, 136.99)
      [0.005, 0.01, 0.015].any? { |d| (((freq - d) * 1000).round % 25).zero? }
    end

  end

end