openscope / openscope

openScope Air Traffic Control Simulator
http://www.openscope.io
Other
699 stars 185 forks source link

Prevent assignment of reserved or nondiscrete beacon codes #1402

Closed JonasStuhlberger closed 5 years ago

JonasStuhlberger commented 5 years ago

Browser: Running the sim locally

Airport: LOWW (WIP Airport, but fully reliable, so that shouldn't be a problem)

Approximate play time before issue: Anytime

Issue Description

When I was testing spawnpatterns at my WIP airport LOWW Vienna, an Austrian Airbus spawned with the squawk code 7600. I'm guessing 7500, 7600 and 7700 are blocked from appearing, but it doesn't seem to work properly

https://files.slack.com/files-pri/T1LML0QF2-FKQHNHK6Y/image.png

Important: No, I did not tell the aircraft to squawk that code.

Thank you.

JonasStuhlberger commented 5 years ago

Another example:

VFR Squawk 7000 image

Marcel510 commented 5 years ago

7000 is the VFR used in most of the european countries. I think at the moment only 1200 is blocked (US VFR Squawk code). Maybe it would be possible to block both of them.

erikquinn commented 5 years ago

Really, only discrete codes should be assigned like this (eg those that do not end in double zeros). It's what makes a discrete code a discrete code... Plus other reserved ones (1202 for gliders and whatever other ones I've forgotten)

oobayly commented 5 years ago

Edit: I've just seen it's being worked on in #1349 although the utilities below would still be compatible.

I thought this one would be a quick fix (as if they actually exist), but I came across a couple of problems:

  1. ~Reserved codes are actually allowed, because they're stored as numbers in RESERVED_SQUAWK_CODES, but generateRandomOctalWithLength returns a string, so they're never matched.~ image
  2. In looking up a definitive list, I noticed different countries and regions have different reserved codes. For example, 1201 is reserved in the USA but not in the UK. If it's going to be fixed, then it probably could be done betterer :-)

Something like this could be done. The ICAO airport code and the transponderCode could be passed to a isDiscreteTransponderCode, where the list of reserved codes can be looked up for the country or region that the airport belongs to

import { REGEX } from '../constants/globalConstants';

/**
 * List of transponder codes that are reserved
 *
 * This enum should be used only during the generation of
 * `AircraftModel` objects.
 *
 * The codes listed should still be assignable at the
 * controler's discretion
 *
 * @property SQUAWK_CODES
 * @type {array<object>}
 * @final
 */
const SQUAWK_CODES = [
    {
        // ICAO
        prefix: null,
        reserved: [
            // Use in Mode S environment
            '1000',
            // When entering SSR area from non-SSR as uncontrolled IFR
            '2000',
            // VFR when no other code has been assigned (eg 1200 is used in the USA)
            '7000',
            // hijack
            '7500',
            // communication failure
            '7600',
            // emergency
            '7700'
        ]
    },
    {
        // Europe
        prefix: /^([elb])/,
        reserved: [
            '0000'
        ]
    },
    {
        // Canda
        prefix: /^c/,
        reserved: [

        ]
    },
    {
        // Belgium
        prefix: /^eb/,
        reserved: [
            /^00(4[1-6]|5[0-7])$/,
            // For testing stations
            '7777'
        ]
    },
    {
        // Germany
        prefix: /^e[dt]/,
        reserved: [
            // Parachute dropping
            '0025',
            // For testing stations
            '7777'
        ]
    },
    {
        // Netherlands
        prefix: /^eh/,
        reserved: [
            // For testing stations
            '7777'
        ]
    },
    {
        // UK
        prefix: /^eg/,
        reserved: [
            // Parachute drop
            '0033'
        ]
    },
    {
        // USA
        prefix: /^k/,
        reserved: [
            // Military intercept
            '0000',
            // VFR - 12xx
            /^12[0-7][0-7]$/,
            // Military & for testing stations
            '7777'
        ]
    },
    {
        // Australia
        prefix: /^y/,
        reserved: [
            // Flights operating at aerodromes (in lieu of codes 1200, 2000 or 3000 when assigned by ATC or noted in the Enroute Supplement)
            '0100'
        ]
    }
];

/**
 * Gets the array of squawk code objects that match the specifed ICAO airport code
 *
 * @method _getCodes
 * @param icao {string}
 * @returns {array}
 */
function _getCodes(icao) {
    return SQUAWK_CODES.filter((item) => {
        return item.prefix === null || item.prefix.test(icao);
    });
}

/**
 * Helper used to test if the `transponderCode` matches the
 * String or RegExp in `against`
 *
 * @param transponderCode {string}
 * @param testAgainst {string|RegExp}
 */
function _isMatch(transponderCode, testAgainst) {
    if (testAgainst instanceof RegExp) {
        return testAgainst.test(transponderCode);
    } else if (typeof testAgainst === 'string') {
        return transponderCode === testAgainst;
    }

    throw new TypeError(
        `Invalid parameter for testAgainst, expected string or RegExp, but got ${typeof testAgainst}`
    );
}

/**
 * Helper used to determine if a given `transponderCode` is reserved
 * in the country or region of the specified `icao` airport code
 *
 * @method _isReserved
 * @param icao {string}
 * @param transponderCode {string}
 * @returns {boolean}
 */
export const isReserved = (icao, transponderCode) => {
    return _getCodes(icao).some((item) => {
        return item.reserved.some((test) => _isMatch(transponderCode, test));
    });
};

/**
 * Boolean helper used to determine if a given `transponderCode` is both
 * the correct length and an octal number.
 *
 * @method isValidTransponderCode
 * @param transponderCode {string}
 * @return {boolean}
 */
export const isValidTransponderCode = (transponderCode) => {
    return REGEX.TRANSPONDER_CODE.test(transponderCode);
};

/**
 * Helper used to determine if a given `transponderCode` is both
 * valid and not in reserved in the country or region of the
 * specified `icao` airport code
 *
 * @method isDiscreteTransponderCode
 * @param icao {string}
 * @param transponderCode {string}
 * @return {boolean}
 */
export const isDiscreteTransponderCode = (icao, transponderCode) => {
    if (!isValidTransponderCode(transponderCode)) {
        return false;
    }

    if (transponderCode.endsWith('00')) {
        return false;
    }

    return !isReserved(icao, transponderCode);
};