Add findCommByFrequencyMHz to Nasal
SF-ID: https://sourceforge.net/p/flightgear/codetickets/2836/
This commit is contained in:
parent
9eccf6b55b
commit
5e1be2d893
8 changed files with 245 additions and 89 deletions
|
@ -1,3 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileName: CommStation.cxx
|
||||
* SPDX-FileComment: class describing a single comm station in the Nav DB
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "CommStation.hxx"
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#ifndef FG_ATC_COMM_STATION_HXX
|
||||
#define FG_ATC_COMM_STATION_HXX
|
||||
/*
|
||||
* SPDX-FileName: CommStation.hxx
|
||||
* SPDX-FileComment: class describing a single comm station in the Nav DB
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Airports/airports_fwd.hxx>
|
||||
#include <Navaids/positioned.hxx>
|
||||
|
@ -24,13 +29,16 @@ public:
|
|||
double freqMHz() const;
|
||||
|
||||
static CommStationRef findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt = NULL);
|
||||
|
||||
static bool isType(FGPositioned::Type ty)
|
||||
{
|
||||
return (ty >= FGPositioned::FREQ_GROUND) && (ty <= FGPositioned::FREQ_UNICOM);
|
||||
}
|
||||
|
||||
private:
|
||||
int mRangeNM;
|
||||
int mFreqKhz;
|
||||
PositionedID mAirport;
|
||||
};
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif // of FG_ATC_COMM_STATION_HXX
|
||||
|
||||
} // namespace flightgear
|
||||
|
|
|
@ -439,6 +439,14 @@ FGPositioned::TypeFilter::TypeFilter(std::initializer_list<Type> types)
|
|||
}
|
||||
}
|
||||
|
||||
FGPositioned::TypeFilter::TypeFilter(Type aMinType, Type aMaxType)
|
||||
{
|
||||
for (int t = aMinType; t <= aMaxType; t++) {
|
||||
addType(static_cast<FGPositioned::Type>(t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FGPositioned::TypeFilter::addType(Type aTy)
|
||||
{
|
||||
if (aTy == INVALID) {
|
||||
|
|
|
@ -44,59 +44,63 @@ class FGPositioned : public SGReferenced
|
|||
public:
|
||||
static const PositionedID TRANSIENT_ID;
|
||||
|
||||
typedef enum {
|
||||
INVALID = 0,
|
||||
AIRPORT,
|
||||
HELIPORT,
|
||||
SEAPORT,
|
||||
RUNWAY,
|
||||
HELIPAD,
|
||||
TAXIWAY,
|
||||
PAVEMENT,
|
||||
WAYPOINT,
|
||||
FIX,
|
||||
NDB,
|
||||
VOR,
|
||||
ILS,
|
||||
LOC,
|
||||
GS,
|
||||
OM,
|
||||
MM,
|
||||
IM,
|
||||
/// important that DME & TACAN are adjacent to keep the TacanFilter
|
||||
/// efficient - DMEs are proxies for TACAN/VORTAC stations
|
||||
DME,
|
||||
TACAN,
|
||||
MOBILE_TACAN,
|
||||
OBSTACLE,
|
||||
/// an actual airport tower - not a radio comms facility!
|
||||
/// some airports have multiple towers, eg EHAM, although our data source
|
||||
/// doesn't necessarily include them
|
||||
TOWER,
|
||||
FREQ_GROUND,
|
||||
FREQ_TOWER,
|
||||
FREQ_ATIS,
|
||||
FREQ_AWOS,
|
||||
FREQ_APP_DEP,
|
||||
FREQ_ENROUTE,
|
||||
FREQ_CLEARANCE,
|
||||
FREQ_UNICOM,
|
||||
// groundnet items
|
||||
PARKING, ///< parking position - might be a gate, or stand
|
||||
TAXI_NODE,
|
||||
// POI items
|
||||
COUNTRY,
|
||||
CITY,
|
||||
TOWN,
|
||||
VILLAGE,
|
||||
|
||||
LAST_TYPE
|
||||
} Type;
|
||||
typedef enum {
|
||||
INVALID = 0,
|
||||
AIRPORT,
|
||||
HELIPORT,
|
||||
SEAPORT,
|
||||
RUNWAY,
|
||||
HELIPAD,
|
||||
TAXIWAY,
|
||||
PAVEMENT,
|
||||
WAYPOINT,
|
||||
FIX,
|
||||
NDB,
|
||||
VOR,
|
||||
ILS,
|
||||
LOC,
|
||||
GS,
|
||||
OM,
|
||||
MM,
|
||||
IM,
|
||||
/// important that DME & TACAN are adjacent to keep the TacanFilter
|
||||
/// efficient - DMEs are proxies for TACAN/VORTAC stations
|
||||
DME,
|
||||
TACAN,
|
||||
MOBILE_TACAN,
|
||||
OBSTACLE,
|
||||
/// an actual airport tower - not a radio comms facility!
|
||||
/// some airports have multiple towers, eg EHAM, although our data source
|
||||
/// doesn't necessarily include them
|
||||
TOWER,
|
||||
//comm stations : if extending this, be sure to update the isType check in
|
||||
// CommStation.hxx
|
||||
FREQ_GROUND,
|
||||
FREQ_TOWER,
|
||||
FREQ_ATIS,
|
||||
FREQ_AWOS,
|
||||
FREQ_APP_DEP,
|
||||
FREQ_ENROUTE,
|
||||
FREQ_CLEARANCE,
|
||||
FREQ_UNICOM,
|
||||
// groundnet items
|
||||
PARKING, ///< parking position - might be a gate, or stand
|
||||
TAXI_NODE,
|
||||
// POI items
|
||||
COUNTRY,
|
||||
CITY,
|
||||
TOWN,
|
||||
VILLAGE,
|
||||
|
||||
virtual ~FGPositioned();
|
||||
|
||||
Type type() const
|
||||
{ return mType; }
|
||||
LAST_TYPE
|
||||
} Type;
|
||||
|
||||
virtual ~FGPositioned();
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
// True for the following types: AIRPORT, HELIPORT, SEAPORT.
|
||||
// False for other types, as well as if pos == nullptr.
|
||||
|
@ -177,7 +181,15 @@ public:
|
|||
TypeFilter(Type aTy = INVALID);
|
||||
|
||||
TypeFilter(std::initializer_list<Type> types);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a new Type Filter based on a sequential range of types
|
||||
*
|
||||
* @param aMinType
|
||||
* @param aMaxType
|
||||
*/
|
||||
TypeFilter(Type aMinType, Type aMaxType);
|
||||
|
||||
bool pass(FGPositioned* aPos) const override;
|
||||
|
||||
Type minType() const override
|
||||
|
|
|
@ -69,6 +69,9 @@ static naGhostType TaxiwayGhostType = { positionedGhostDestroy, "taxiway", runwa
|
|||
static const char* fixGhostGetMember(naContext c, void* g, naRef field, naRef* out);
|
||||
static naGhostType FixGhostType = { positionedGhostDestroy, "fix", fixGhostGetMember, nullptr };
|
||||
|
||||
static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef* out);
|
||||
static naGhostType CommGhostType = {positionedGhostDestroy, "comm", commGhostGetMember, nullptr};
|
||||
|
||||
static void hashset(naContext c, naRef hash, const char* key, naRef val)
|
||||
{
|
||||
naRef s = naNewString(c);
|
||||
|
@ -88,8 +91,8 @@ FGPositioned* positionedGhost(naRef r)
|
|||
if ((naGhost_type(r) == &AirportGhostType) ||
|
||||
(naGhost_type(r) == &NavaidGhostType) ||
|
||||
(naGhost_type(r) == &RunwayGhostType) ||
|
||||
(naGhost_type(r) == &FixGhostType))
|
||||
{
|
||||
(naGhost_type(r) == &FixGhostType) ||
|
||||
(naGhost_type(r) == &CommGhostType)) {
|
||||
return (FGPositioned*) naGhost_ptr(r);
|
||||
}
|
||||
|
||||
|
@ -131,6 +134,12 @@ static FGFix* fixGhost(naRef r)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static flightgear::CommStation* commGhost(naRef r)
|
||||
{
|
||||
if (naGhost_type(r) == &CommGhostType)
|
||||
return (flightgear::CommStation*)naGhost_ptr(r);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void positionedGhostDestroy(void* g)
|
||||
{
|
||||
|
@ -203,6 +212,16 @@ naRef ghostForFix(naContext c, const FGFix* r)
|
|||
return naNewGhost2(c, &FixGhostType, (void*) r);
|
||||
}
|
||||
|
||||
naRef ghostForComm(naContext c, const flightgear::CommStation* comm)
|
||||
{
|
||||
if (!c) {
|
||||
return naNil();
|
||||
}
|
||||
|
||||
FGPositioned::get(comm); // take a ref
|
||||
return naNewGhost2(c, &CommGhostType, (void*)comm);
|
||||
}
|
||||
|
||||
naRef ghostForPositioned(naContext c, FGPositionedRef pos)
|
||||
{
|
||||
if (!pos) {
|
||||
|
@ -222,6 +241,16 @@ naRef ghostForPositioned(naContext c, FGPositionedRef pos)
|
|||
return ghostForHelipad(c, fgpositioned_cast<FGHelipad>(pos));
|
||||
case FGPositioned::RUNWAY:
|
||||
return ghostForRunway(c, fgpositioned_cast<FGRunway>(pos));
|
||||
case FGPositioned::FREQ_APP_DEP:
|
||||
case FGPositioned::FREQ_AWOS:
|
||||
case FGPositioned::FREQ_GROUND:
|
||||
case FGPositioned::FREQ_TOWER:
|
||||
case FGPositioned::FREQ_ATIS:
|
||||
case FGPositioned::FREQ_CLEARANCE:
|
||||
case FGPositioned::FREQ_UNICOM:
|
||||
case FGPositioned::FREQ_ENROUTE:
|
||||
return ghostForComm(c, fgpositioned_cast<flightgear::CommStation>(pos));
|
||||
|
||||
default:
|
||||
SG_LOG(SG_NASAL, SG_DEV_ALERT, "Type lacks Nasal ghost mapping:" << pos->typeString());
|
||||
return naNil();
|
||||
|
@ -387,6 +416,31 @@ static const char* fixGhostGetMember(naContext c, void* g, naRef field, naRef* o
|
|||
return "";
|
||||
}
|
||||
|
||||
static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef* out)
|
||||
{
|
||||
const char* fieldName = naStr_data(field);
|
||||
auto comm = static_cast<flightgear::CommStation*>(g);
|
||||
|
||||
if (!strcmp(fieldName, "id"))
|
||||
*out = stringToNasal(c, comm->ident());
|
||||
else if (!strcmp(fieldName, "lat"))
|
||||
*out = naNum(comm->latitude());
|
||||
else if (!strcmp(fieldName, "lon"))
|
||||
*out = naNum(comm->longitude());
|
||||
else if (!strcmp(fieldName, "type")) {
|
||||
*out = stringToNasal(c, comm->nameForType(comm->type()));
|
||||
} else if (!strcmp(fieldName, "name"))
|
||||
*out = stringToNasal(c, comm->name());
|
||||
else if (!strcmp(fieldName, "frequency")) {
|
||||
*out = naNum(comm->freqMHz());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
static bool hashIsCoord(naRef h)
|
||||
{
|
||||
naRef parents = naHash_cget(h, (char*) "parents");
|
||||
|
@ -1364,6 +1418,34 @@ static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* ar
|
|||
return r;
|
||||
}
|
||||
|
||||
static naRef f_findCommByFrequency(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
int argOffset = 0;
|
||||
SGGeod pos = globals->get_aircraft_position();
|
||||
argOffset += geodFromArgs(args, 0, argc, pos);
|
||||
|
||||
if (!naIsNum(args[argOffset])) {
|
||||
naRuntimeError(c, "findCommByFrequencyMhz expectes frequency (in Mhz) as arg %d", argOffset);
|
||||
}
|
||||
|
||||
// initial filter is all comm types
|
||||
FGPositioned::TypeFilter filter(FGPositioned::FREQ_GROUND, FGPositioned::FREQ_UNICOM);
|
||||
|
||||
double freqMhz = args[argOffset++].num;
|
||||
if (argOffset < argc) {
|
||||
// allow specifying an explicitly type by name
|
||||
filter = FGPositioned::TypeFilter{FGPositioned::typeFromName(naStr_data(args[argOffset]))};
|
||||
}
|
||||
|
||||
auto ref = NavDataCache::instance()->findCommByFreq(static_cast<int>(freqMhz * 1000), pos, &filter);
|
||||
if (!ref) {
|
||||
return naNil();
|
||||
}
|
||||
|
||||
auto comm = fgpositioned_cast<flightgear::CommStation>(ref);
|
||||
return ghostForComm(c, comm);
|
||||
}
|
||||
|
||||
static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
int argOffset = 0;
|
||||
|
@ -1600,33 +1682,36 @@ FGPositionedRef positionedFromArg(naRef ref)
|
|||
}
|
||||
|
||||
// Table of extension functions. Terminate with zeros.
|
||||
static struct { const char* name; naCFunction func; } funcs[] = {
|
||||
{ "carttogeod", f_carttogeod },
|
||||
{ "geodtocart", f_geodtocart },
|
||||
{ "geodinfo", f_geodinfo },
|
||||
{ "formatLatLon", f_formatLatLon },
|
||||
{ "parseStringAsLatLonValue", f_parseStringAsLatLonValue},
|
||||
{ "get_cart_ground_intersection", f_get_cart_ground_intersection },
|
||||
{ "aircraftToCart", f_aircraftToCart },
|
||||
{ "airportinfo", f_airportinfo },
|
||||
{ "findAirportsWithinRange", f_findAirportsWithinRange },
|
||||
{ "findAirportsByICAO", f_findAirportsByICAO },
|
||||
{ "navinfo", f_navinfo },
|
||||
{ "findNavaidsWithinRange", f_findNavaidsWithinRange },
|
||||
{ "findNDBByFrequencyKHz", f_findNDBByFrequency },
|
||||
{ "findNDBsByFrequencyKHz", f_findNDBsByFrequency },
|
||||
{ "findNavaidByFrequencyMHz", f_findNavaidByFrequency },
|
||||
{ "findNavaidsByFrequencyMHz", f_findNavaidsByFrequency },
|
||||
{ "findNavaidsByID", f_findNavaidsByIdent },
|
||||
{ "findFixesByID", f_findFixesByIdent },
|
||||
{ "findByIdent", f_findByIdent },
|
||||
{ "magvar", f_magvar },
|
||||
{ "courseAndDistance", f_courseAndDistance },
|
||||
{ "greatCircleMove", f_greatCircleMove },
|
||||
{ "tileIndex", f_tileIndex },
|
||||
{ "tilePath", f_tilePath },
|
||||
{ 0, 0 }
|
||||
};
|
||||
static struct {
|
||||
const char* name;
|
||||
naCFunction func;
|
||||
} funcs[] = {
|
||||
{"carttogeod", f_carttogeod},
|
||||
{"geodtocart", f_geodtocart},
|
||||
{"geodinfo", f_geodinfo},
|
||||
{"formatLatLon", f_formatLatLon},
|
||||
{"parseStringAsLatLonValue", f_parseStringAsLatLonValue},
|
||||
{"get_cart_ground_intersection", f_get_cart_ground_intersection},
|
||||
{"aircraftToCart", f_aircraftToCart},
|
||||
{"airportinfo", f_airportinfo},
|
||||
{"findAirportsWithinRange", f_findAirportsWithinRange},
|
||||
{"findAirportsByICAO", f_findAirportsByICAO},
|
||||
{"navinfo", f_navinfo},
|
||||
{"findNavaidsWithinRange", f_findNavaidsWithinRange},
|
||||
{"findNDBByFrequencyKHz", f_findNDBByFrequency},
|
||||
{"findNDBsByFrequencyKHz", f_findNDBsByFrequency},
|
||||
{"findNavaidByFrequencyMHz", f_findNavaidByFrequency},
|
||||
{"findNavaidsByFrequencyMHz", f_findNavaidsByFrequency},
|
||||
{"findCommByFrequencyMHz", f_findCommByFrequency},
|
||||
{"findNavaidsByID", f_findNavaidsByIdent},
|
||||
{"findFixesByID", f_findFixesByIdent},
|
||||
{"findByIdent", f_findByIdent},
|
||||
{"magvar", f_magvar},
|
||||
{"courseAndDistance", f_courseAndDistance},
|
||||
{"greatCircleMove", f_greatCircleMove},
|
||||
{"tileIndex", f_tileIndex},
|
||||
{"tilePath", f_tilePath},
|
||||
{0, 0}};
|
||||
|
||||
|
||||
naRef initNasalPositioned(naRef globals, naContext c)
|
||||
|
|
|
@ -29,7 +29,7 @@ void CommandsTests::setUp()
|
|||
|
||||
void CommandsTests::tearDown()
|
||||
{
|
||||
delete SGCommandMgr::instance();
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void CommandsTests::testPropertyAdjustCommand()
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <Main/globals.hxx>
|
||||
#include <Main/util.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
#include <Main/FGInterpolator.hxx>
|
||||
|
@ -59,6 +61,12 @@ void NasalSysTests::tearDown()
|
|||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
bool NasalSysTests::checkNoNasalErrors()
|
||||
{
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
auto errors = nasalSys->getAndClearErrorList();
|
||||
return errors.empty();
|
||||
}
|
||||
|
||||
// Test test
|
||||
void NasalSysTests::testStructEquality()
|
||||
|
@ -169,6 +177,31 @@ void NasalSysTests::testAirportGhost()
|
|||
|
||||
}
|
||||
|
||||
void NasalSysTests::testFindComm()
|
||||
{
|
||||
FGAirportRef apt = FGAirport::getByIdent("EDDM");
|
||||
FGTestApi::setPositionAndStabilise(apt->geod());
|
||||
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
nasalSys->getAndClearErrorList();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var comm = findCommByFrequencyMHz(123.125);
|
||||
unitTest.assert_equal(comm.id, "ATIS");
|
||||
|
||||
# explicit filter, should't match
|
||||
var noComm = findCommByFrequencyMHz(123.125, "tower");
|
||||
unitTest.assert_equal(noComm, nil);
|
||||
|
||||
# match with filter
|
||||
var comm2 = findCommByFrequencyMHz(121.725, "clearance");
|
||||
unitTest.assert_equal(comm2.id, "CLNC DEL");
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok && checkNoNasalErrors());
|
||||
}
|
||||
|
||||
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2246/
|
||||
|
||||
void NasalSysTests::testCompileLarge()
|
||||
|
|
|
@ -40,8 +40,11 @@ class NasalSysTests : public CppUnit::TestFixture
|
|||
CPPUNIT_TEST(testKeywordArgInHash);
|
||||
CPPUNIT_TEST(testNullAccess);
|
||||
CPPUNIT_TEST(testNullishChain);
|
||||
CPPUNIT_TEST(testFindComm);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
bool checkNoNasalErrors();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
@ -59,6 +62,7 @@ public:
|
|||
void testKeywordArgInHash();
|
||||
void testNullAccess();
|
||||
void testNullishChain();
|
||||
void testFindComm();
|
||||
};
|
||||
|
||||
#endif // _FG_NASALSYS_UNIT_TESTS_HXX
|
||||
|
|
Loading…
Reference in a new issue