lib/aixm/feature/navigational_aid/vor.rb

Summary

Maintainability
A
0 mins
Test Coverage
using AIXM::Refinements

module AIXM
  class Feature
    class NavigationalAid

      # VHF omni directional radio range (VOR) is a type of radio navigation for
      # aircraft to determine their position and course. They operate in the
      # frequency band between 108.00 Mhz to 117.95 MHz.
      #
      # ===Cheat Sheet in Pseudo Code:
      #   vor = AIXM.vor(
      #     source: String or nil
      #     region: String or nil
      #     organisation: AIXM.organisation
      #     id: String
      #     name: String
      #     xy: AIXM.xy
      #     z: AIXM.z or nil
      #     type: TYPES
      #     f: AIXM.f
      #     north: NORTHS
      #   )
      #   vor.timetable = AIXM.timetable or nil
      #   vor.remarks = String or nil
      #   vor.comment = Object or nil
      #   vor.associate_dme     # turns the VOR into a VOR/DME
      #   vor.associate_tacan   # turns the VOR into a VORTAC
      #
      # @see https://gitlab.com/openflightmaps/ofmx/wikis/Navigational-aid#vor-vor
      class VOR < NavigationalAid
        public_class_method :new

        TYPES = {
          VOR: :conventional,
          DVOR: :doppler,
          OTHER: :other         # specify in remarks
        }.freeze

        NORTHS = {
          TRUE: :geographic,
          GRID: :grid,         # parallel to the north-south lines of the UTM grid
          MAG: :magnetic,
          OTHER: :other        # specify in remarks
        }.freeze

        # @!method dme
        #   @return [AIXM::Feature::NavigationalAid::DME, nil] associated DME
        #
        # @!method dme=(dme)
        #   @param dme [AIXM::Feature::NavigationalAid::DME, nil]
        has_one :dme, allow_nil: true

        # @!method tacan
        #   @return [AIXM::Feature::NavigationalAid::TACAN, nil] associated TACAN
        #
        # @!method tacan=(tacan)
        #   @param tacan [AIXM::Feature::NavigationalAid::TACAN, nil]
        has_one :tacan, allow_nil: true

        # Type of VOR
        #
        # @overload type
        #   @return [Symbol] any of {TYPES}
        # @overload type=(value)
        #   @param value [Symbol] any of {TYPES}
        attr_reader :type

        # Radio requency
        #
        # @overload f
        #   @return [AIXM::F]
        # @overload f=(value)
        #   @param value [AIXM::F]
        attr_reader :f

        # North indication
        #
        # @overload north
        #   @return [Symbol] any of {NORTHS}
        # @overload north=(value)
        #   @param value [Symbol] any of {NORTHS}
        attr_reader :north

        # See the {cheat sheet}[AIXM::Feature::VOR] for examples on how to
        # create instances of this class.
        def initialize(type:, f:, north:, **arguments)
          super(**arguments)
          self.type, self.f, self.north = type, f, north
        end

        def type=(value)
          @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
        end

        def f=(value)
          fail(ArgumentError, "invalid f") unless value.is_a?(F) && value.between?(108, 117.95, :mhz)
          @f = value
        end

        def north=(value)
          @north = NORTHS.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid north")
        end

        # @!method associate_dme
        #   Build a DME associated to this VOR (which turns it into a VOR/DME)
        #
        #   @return [AIXM::Feature::NavigationalAid::DME] associated DME
        #
        # @!method dassociate_tacan
        #   Build a TACAN associated to this VOR (which turns it into a VORTAC)
        #
        #   @return [AIXM::Feature::NavigationalAid::TACAN] associated TACAN
        %i(dme tacan).each do |secondary|
          define_method("associate_#{secondary}") do
            send("#{secondary}=",
              AIXM.send(secondary,
                region: region,
                source: source,
                organisation: organisation,
                id: id,
                name: name,
                xy: xy,
                z: z,
                ghost_f: f
              ).tap do |navigational_aid|
                navigational_aid.timetable = timetable
                navigational_aid.remarks = remarks
              end
            )
          end
        end

        # @!visibility private
        def add_uid_to(builder)
          builder.VorUid({ region: (region if AIXM.ofmx?) }.compact) do |vor_uid|
            vor_uid.codeId(id)
            vor_uid.geoLat(xy.lat(AIXM.schema))
            vor_uid.geoLong(xy.long(AIXM.schema))
          end
        end

        # @!visibility private
        def add_to(builder)
          super
          builder.Vor({ source: (source if AIXM.ofmx?) }.compact) do |vor|
            vor.comment(indented_comment) if comment
            add_uid_to(vor)
            organisation.add_uid_to(vor)
            vor.txtName(name) if name
            vor.codeType(type_key)
            vor.valFreq(f.freq.trim)
            vor.uomFreq(f.unit.upcase)
            vor.codeTypeNorth(north_key)
            vor.codeDatum('WGE')
            if z
              vor.valElev(z.alt)
              vor.uomDistVer(z.unit.upcase)
            end
            timetable.add_to(vor, as: :Vtt) if timetable
            vor.txtRmk(remarks) if remarks
          end
          @dme.add_to(builder) if @dme
          @tacan.add_to(builder) if @tacan
        end

        # @api private
        def type_key
          TYPES.key(type)
        end

        # @api private
        def north_key
          NORTHS.key(north)
        end
      end

    end
  end
end