diff --git a/MATLAB/+XPlaneConnect/sendDREF.m b/MATLAB/+XPlaneConnect/sendDREF.m index cbfacb3..e9a1b5c 100644 --- a/MATLAB/+XPlaneConnect/sendDREF.m +++ b/MATLAB/+XPlaneConnect/sendDREF.m @@ -15,5 +15,4 @@ if ~exist('socket', 'var') sendDREFs(dref, value) else sendDREFs(dref, value, socket) -end -socket.sendDREF(dref, value); \ No newline at end of file +end \ No newline at end of file diff --git a/README.md b/README.md index 0065c56..774afc9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The X-Plane Connect (XPC) Toolbox is an open source research tool used to interact with the commercial flight simulator software X-Plane. XPC allows users to control aircraft and receive state information from aircraft simulated in -X-Plane using functions written in C, C++, Java, or MATLAB in real time over the +X-Plane using functions written in C, C++, Java, MATLAB, or Python in real time over the network. This research tool has been used to visualize flight paths, test control algorithms, simulate an active airspace, or generate out-the-window visuals for in-house flight simulation software. Possible applications include active control diff --git a/TestScripts/C Tests.xcodeproj/project.pbxproj b/TestScripts/C Tests.xcodeproj/project.pbxproj index 6235e48..a933529 100644 --- a/TestScripts/C Tests.xcodeproj/project.pbxproj +++ b/TestScripts/C Tests.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */ = {isa = PBXBuildFile; fileRef = BE7CF62B1B0CFA34008B1E07 /* Test.c */; }; BEB0F5071A28F9A3001975A6 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F5061A28F9A3001975A6 /* main.c */; }; BEB0F5091A28F9A3001975A6 /* C_Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = BEB0F5081A28F9A3001975A6 /* C_Tests.1 */; }; BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F50F1A28F9D5001975A6 /* xplaneConnect.c */; }; @@ -27,6 +28,17 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + BE7CF6261B0CFA34008B1E07 /* CtrlTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CtrlTests.h; sourceTree = ""; }; + BE7CF6271B0CFA34008B1E07 /* DataTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataTests.h; sourceTree = ""; }; + BE7CF6281B0CFA34008B1E07 /* DrefTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrefTests.h; sourceTree = ""; }; + BE7CF6291B0CFA34008B1E07 /* PosiTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PosiTests.h; sourceTree = ""; }; + BE7CF62A1B0CFA34008B1E07 /* SimuTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimuTests.h; sourceTree = ""; }; + BE7CF62B1B0CFA34008B1E07 /* Test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Test.c; sourceTree = ""; }; + BE7CF62C1B0CFA34008B1E07 /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = ""; }; + BE7CF62D1B0CFA34008B1E07 /* TextTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextTests.h; sourceTree = ""; }; + BE7CF62E1B0CFA34008B1E07 /* UDPTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UDPTests.h; sourceTree = ""; }; + BE7CF62F1B0CFA34008B1E07 /* ViewTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewTests.h; sourceTree = ""; }; + BE7CF6301B0CFA34008B1E07 /* WyptTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WyptTests.h; sourceTree = ""; }; BEB0F5031A28F9A3001975A6 /* C Tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "C Tests"; sourceTree = BUILT_PRODUCTS_DIR; }; BEB0F5061A28F9A3001975A6 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; BEB0F5081A28F9A3001975A6 /* C_Tests.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = C_Tests.1; sourceTree = ""; }; @@ -66,6 +78,17 @@ BEB0F5051A28F9A3001975A6 /* C Tests */ = { isa = PBXGroup; children = ( + BE7CF6261B0CFA34008B1E07 /* CtrlTests.h */, + BE7CF6271B0CFA34008B1E07 /* DataTests.h */, + BE7CF6281B0CFA34008B1E07 /* DrefTests.h */, + BE7CF6291B0CFA34008B1E07 /* PosiTests.h */, + BE7CF62A1B0CFA34008B1E07 /* SimuTests.h */, + BE7CF62B1B0CFA34008B1E07 /* Test.c */, + BE7CF62C1B0CFA34008B1E07 /* Test.h */, + BE7CF62D1B0CFA34008B1E07 /* TextTests.h */, + BE7CF62E1B0CFA34008B1E07 /* UDPTests.h */, + BE7CF62F1B0CFA34008B1E07 /* ViewTests.h */, + BE7CF6301B0CFA34008B1E07 /* WyptTests.h */, BEB0F5061A28F9A3001975A6 /* main.c */, BEB0F5081A28F9A3001975A6 /* C_Tests.1 */, ); @@ -126,6 +149,7 @@ BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */, BEB0F5121A28F9D5001975A6 /* xplaneConnect.h in Sources */, BEB0F5071A28F9A3001975A6 /* main.c in Sources */, + BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/TestScripts/C Tests/CtrlTests.h b/TestScripts/C Tests/CtrlTests.h index 3465f70..ccf0569 100644 --- a/TestScripts/C Tests/CtrlTests.h +++ b/TestScripts/C Tests/CtrlTests.h @@ -74,6 +74,7 @@ int basicCTRLTest(char** drefs, int ac) { return -30000 + result; } + return 0; } int testCTRL_Player() @@ -143,5 +144,6 @@ int testCTRL_Speedbrakes() { return -30000 + result; } + return 0; } #endif \ No newline at end of file diff --git a/TestScripts/C Tests/PosiTests.h b/TestScripts/C Tests/PosiTests.h index dfc7e07..d8b5673 100644 --- a/TestScripts/C Tests/PosiTests.h +++ b/TestScripts/C Tests/PosiTests.h @@ -83,6 +83,7 @@ int basicPOSITest(char** drefs, int ac) { return -20000 + result; } + return 0; } int testPOSI_Player() diff --git a/TestScripts/C Tests/SimuTests.h b/TestScripts/C Tests/SimuTests.h index 1c807b7..0d12012 100644 --- a/TestScripts/C Tests/SimuTests.h +++ b/TestScripts/C Tests/SimuTests.h @@ -31,6 +31,7 @@ int doSIMUTest(int value, float expected) return -100 - i; } } + return 0; } int testSIMU_Basic() diff --git a/TestScripts/MATLAB Tests/tests.m b/TestScripts/MATLAB Tests/tests.m index 11c8800..13e0be4 100644 --- a/TestScripts/MATLAB Tests/tests.m +++ b/TestScripts/MATLAB Tests/tests.m @@ -1,3 +1,6 @@ +addpath('../../MATLAB') +import XPlaneConnect.* + testsPassed=0; testsFailed=0; if ismac() @@ -22,6 +25,7 @@ theTests = {{@openCloseTest, 'Open/Close Test', 0},... {@pauseTest,'Pause Test', 0},... {@setConnTest, 'setConn Test', 0}}; +socket = openUDP(); for i=1:length(theTests) fprintf(['Test ',num2str(i),': ',theTests{i}{2},' - ']); try @@ -36,6 +40,7 @@ for i=1:length(theTests) testsFailed = testsFailed + 1; end end +closeUDP(socket); disp('Results Summary:'); fprintf('Passed: %i\tFailed: %i\n',testsPassed, testsFailed); \ No newline at end of file diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp index 23a07b5..42048e5 100644 --- a/xpcPlugin/DataManager.cpp +++ b/xpcPlugin/DataManager.cpp @@ -313,12 +313,12 @@ namespace XPC } if (!xdref) // DREF does not exist { - Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref); + Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str()); return 0; } XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); - Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %s (x:%X) Type: %i", dref, xdref, dataType); + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType); // XPLMDataTypeID is a bit flag, so it may contain more than one of the // following types. We prefer types as close to float as possible. if ((dataType & 2) == 2) // Float @@ -518,7 +518,7 @@ namespace XPC if (!xdref) { // DREF does not exist - Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref); + Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str()); return; } if (isnan(values[0])) diff --git a/xpcPlugin/Log.cpp b/xpcPlugin/Log.cpp index a9898b4..411cdb4 100644 --- a/xpcPlugin/Log.cpp +++ b/xpcPlugin/Log.cpp @@ -8,7 +8,9 @@ #include #include +#ifndef LIN #include +#endif #include #include @@ -20,6 +22,20 @@ namespace XPC static std::FILE* fd; static void WriteTime(FILE* fd) { +#ifdef LIN + // Can't provide high resolution logging on Linux because C++11 doesn't work with X-Plane. + time_t rawtime; + tm* timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + + char buffer[16] = { 0 }; + // Format is equivalent to [%F %T], but neither of those specifiers is + // supported on Windows as of Visual Studio 13 + strftime(buffer, 16, "[%H:%M:%S] ", timeinfo); + + fprintf(fd, buffer); +#else using namespace std::chrono; system_clock::time_point now = system_clock::now(); @@ -36,6 +52,7 @@ namespace XPC << std::setw(3) << ms.count() << "|"; std::fprintf(fd, ss.str().c_str()); +#endif } static void WriteLevel(FILE* fd, int level) diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp index bcc5db5..26dca40 100644 --- a/xpcPlugin/Message.cpp +++ b/xpcPlugin/Message.cpp @@ -3,6 +3,8 @@ #include "Message.h" #include "Log.h" +#include + #include #include #include @@ -53,19 +55,19 @@ namespace XPC void Message::PrintToLog() const { - std::stringstream ss; + using namespace std; + stringstream ss; // Dump raw bytes to string - ss << std::hex << std::setfill('0'); + ss << hex << setfill('0'); for (int i = 0; i < size; ++i) { - ss << ' ' << std::setw(2) << static_cast(buffer[i]); + ss << ' ' << setw(2) << static_cast(buffer[i]); } Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - ss << std::dec; ss.str(""); - ss << "Head:" << GetHead() << " Size:" << GetSize(); + ss << "Head: " << GetHead() << "(0x" << setw(8) << GetMagicNumber() << ")" << dec << " Size: " << GetSize(); switch (GetMagicNumber()) // Binary version of head { case 0x4E4EF443: // CONN @@ -96,12 +98,12 @@ namespace XPC } case 0x41544144: // DATA { - std::size_t numCols = (size - 5) / 36; + size_t numCols = (size - 5) / 36; float values[32][9]; for (int i = 0; i < numCols; ++i) { values[i][0] = buffer[5 + 36 * i]; - std::memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); + memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); } ss << " (" << numCols << " lines)"; Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); @@ -120,8 +122,8 @@ namespace XPC case 0x46455244: // DREF { Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - std::string dref((char*)buffer + 6, buffer[5]); - Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref); + string dref((char*)buffer + 6, buffer[5]); + Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref.c_str()); ss.str(""); int values = buffer[6 + buffer[5]]; ss << " Values(size " << values << ") ="; @@ -138,9 +140,9 @@ namespace XPC int cur = 6; for (int i = 0; i < buffer[5]; ++i) { - std::string dref((char*)buffer + cur + 1, buffer[cur]); + string dref((char*)buffer + cur + 1, buffer[cur]); Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s", - i + 1, buffer[5], dref.length(), dref); + i + 1, buffer[5], dref.length(), dref.c_str()); cur += 1 + buffer[cur]; } break; @@ -151,8 +153,8 @@ namespace XPC float gear = *((float*)(buffer + 30)); float pos[3]; float orient[3]; - std::memcpy(pos, buffer + 6, 12); - std::memcpy(orient, buffer + 18, 12); + memcpy(pos, buffer + 6, 12); + memcpy(orient, buffer + 18, 12); ss << " AC:" << (int)aircraft; ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:("; ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:"; diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index 4f7c3d3..d72b8a8 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -89,7 +89,7 @@ namespace XPC // Set current connection sockaddr sourceaddr = msg.GetSource(); connectionKey = UDPSocket::GetHost(&sourceaddr); - Log::FormatLine(LOG_INFO, "MSGH", "Handling message from %s", connectionKey); + Log::FormatLine(LOG_INFO, "MSGH", "Handling message from %s", connectionKey.c_str()); std::map::iterator conn = connections.find(connectionKey); if (conn == connections.end()) // New connection { @@ -102,13 +102,13 @@ namespace XPC connection.getdCount = 0; connections[connectionKey] = connection; Log::FormatLine(LOG_DEBUG, "MSGH", "New connection. ID=%u, Remote=%s", - connection.id, connectionKey); + connection.id, connectionKey.c_str()); } else { connection = (*conn).second; Log::FormatLine(LOG_DEBUG, "MSGH", "Existing connection. ID=%u, Remote=%s", - connection.id, connectionKey); + connection.id, connectionKey.c_str()); } msg.PrintToLog(); @@ -417,7 +417,7 @@ namespace XPC pos += 4 * valueCount; DataManager::Set(dref, values, valueCount); - Log::FormatLine(LOG_DEBUG, "DREF", "Set %d values for %s", valueCount, dref); + Log::FormatLine(LOG_DEBUG, "DREF", "Set %d values for %s", valueCount, dref.c_str()); } if (pos != size) { diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index 8a1aa36..349f127 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -38,7 +38,7 @@ namespace XPC #elif (__APPLE__ || __linux) if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - Log::WriteLine("[SOCK] ERROR: Failed to open socket"); + Log::WriteLine(LOG_FATAL, tag, "ERROR: Failed to open socket"); return; } int optval = 1; diff --git a/xpcPlugin/XPlaneConnect/64/lin.xpl b/xpcPlugin/XPlaneConnect/64/lin.xpl index 82d335a..9fe5127 100755 Binary files a/xpcPlugin/XPlaneConnect/64/lin.xpl and b/xpcPlugin/XPlaneConnect/64/lin.xpl differ diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index 88b4c11..a27a63b 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/lin.xpl b/xpcPlugin/XPlaneConnect/lin.xpl index 67f6ccb..09f3158 100755 Binary files a/xpcPlugin/XPlaneConnect/lin.xpl and b/xpcPlugin/XPlaneConnect/lin.xpl differ diff --git a/xpcPlugin/XPlaneConnect/mac.xpl b/xpcPlugin/XPlaneConnect/mac.xpl old mode 100644 new mode 100755 index 1e80649..da391d2 Binary files a/xpcPlugin/XPlaneConnect/mac.xpl and b/xpcPlugin/XPlaneConnect/mac.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index 30b6838..ce96503 100644 Binary files a/xpcPlugin/XPlaneConnect/win.xpl and b/xpcPlugin/XPlaneConnect/win.xpl differ diff --git a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj index 4bca25c..4dbd663 100755 --- a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj +++ b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ BE37D960187C8B0F0033B082 /* XPCPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */; }; BE8361EF18C5591C00E9C923 /* mac.xpl in CopyFiles */ = {isa = PBXBuildFile; fileRef = D607B19909A556E400699BC3 /* mac.xpl */; }; BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */; }; - BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */; }; BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */; }; BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD311AE041A3007BA7DA /* Log.cpp */; }; BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD331AE041A3007BA7DA /* Message.cpp */; }; @@ -40,8 +39,6 @@ BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XPCPlugin.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; }; BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataManager.cpp; sourceTree = ""; }; BEABAD2C1AE041A3007BA7DA /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataManager.h; sourceTree = ""; }; - BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataMaps.cpp; sourceTree = ""; }; - BEABAD2E1AE041A3007BA7DA /* DataMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataMaps.h; sourceTree = ""; }; BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drawing.cpp; sourceTree = ""; }; BEABAD301AE041A3007BA7DA /* Drawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Drawing.h; sourceTree = ""; }; BEABAD311AE041A3007BA7DA /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = ""; }; @@ -82,7 +79,6 @@ BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */, BEDC620218EDF1A7005DB364 /* xplaneConnect.c */, BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */, - BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */, BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */, BEABAD311AE041A3007BA7DA /* Log.cpp */, BEABAD331AE041A3007BA7DA /* Message.cpp */, @@ -97,7 +93,6 @@ children = ( BEDC620318EDF1A7005DB364 /* xplaneConnect.h */, BEABAD2C1AE041A3007BA7DA /* DataManager.h */, - BEABAD2E1AE041A3007BA7DA /* DataMaps.h */, BEABAD301AE041A3007BA7DA /* Drawing.h */, BEABAD321AE041A3007BA7DA /* Log.h */, BEABAD341AE041A3007BA7DA /* Message.h */, @@ -192,7 +187,6 @@ BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */, BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */, BEABAD3C1AE041A3007BA7DA /* MessageHandlers.cpp in Sources */, - BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */, BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */, BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */, BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */,