/** * libicsneo A2B example * * Example were made to be ran with RAD-A2B as main node on TDM4 16 bit channel size and one additional sub node (either an ADI board or an additional RAD-A2B). * Follow the specific hardware instructions per example to ensure expected output. Be sure to configure the A2B network before running these examples, especially * ones which Tx or Rx audio. * * Options: * -h, --help Display help message. * -e, --example [EXAMPLE_NUM] Example to run. * Example usage: ./libicsneocpp-a2b.exe --example 1 * Example usage: ./libicsneocpp-a2b.exe -h */ #include #include #include #include #include #include #include std::string makeWAV() { icsneo::WAVHeader header = icsneo::WAVHeader(1, 48000, 16); std::vector sineWAVSamples = { 0xFF, 0x3F, 0x81, 0x5A, 0xD9, 0x6E, 0xA2, 0x7B, 0xFF, 0x7F, 0xA2, 0x7B, 0xD9, 0x6E, 0x81, 0x5A, 0xFF, 0x3F, 0x20, 0x21, 0x00, 0x00, 0xE0, 0xDE, 0x01, 0xC0, 0x7F, 0xA5, 0x27, 0x91, 0x5E, 0x84, 0x01, 0x80, 0x5E, 0x84, 0x27, 0x91, 0x7F, 0xA5, 0x01, 0xC0, 0xE0, 0xDe, 0x00, 0x00, 0x20, 0x21 }; std::vector sineWAV; sineWAV.reserve(sineWAVSamples.size() + sizeof(header)); sineWAV.insert(sineWAV.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header)); std::copy(sineWAVSamples.begin(), sineWAVSamples.end(), std::back_inserter(sineWAV)); return std::string(sineWAV.begin(), sineWAV.end()); } /** * Example 0: TX */ void example0(const std::shared_ptr& rada2b) { std::cout << "Transmitting a sine tone..." << std::endl; // Create sine tone in wav format std::string wavString = makeWAV(); std::istringstream sineWAV(wavString); double elapsedTime = 0.0; // Create a IWAVStream object which represents a WAV data stream // the IWAVStream object here is initialized with an outside std::ostream, // so it holds a reference pointer to this stream. icsneo::IWAVStream wavStream(sineWAV); // Create a channel map which maps each message channel to a channel in the input WAV file icsneo::ChannelMap channelMap; // Here we will just set every message channel to channel 0 in the WAV file // We have 8 channels since this is TDM4 and we include both upstream and downstream // see docs for specific message channel labeling information for(uint8_t messageChannel = 0; messageChannel < 8; messageChannel++) { channelMap[messageChannel] = 0; } // Play roughly 5 seconds of sine tone. while(elapsedTime < 5.0) { while(elapsedTime < 5.0) { // If WAVStream is invalid (at EOF) break out of loop if(!wavStream) { break; } // Creates a new message with the maximum amount of allocated frames auto msg = std::make_shared( icsneo::A2BMessage::TDMMode::TDM4, /* TDM mode of the message, we use TDM4 for this whole example*/ true /* true if we want 16 bit channels in the message, false for 32 bit. This should match the RAD-A2B device setting */ ); msg->txmsg = true; msg->network = icsneo::Network(icsneo::Network::NetID::A2B_02); // Load the WAV audio data into the desired channel, break if we fail to load if(!msg->loadAudioBuffer(wavStream, channelMap)) { break; } // Transmit the message if(!rada2b->transmit(msg)) { std::cout << "Failed to transmit." << std::endl; break; } elapsedTime += (static_cast(msg->getNumFrames()))*1.0/48000.0; } // Reset the WAV stream wavStream.reset(); } } /** * Example 1: RX */ void example1(const std::shared_ptr& rada2b) { std::cout << "Receiving 5 seconds of audio data..." << std::endl; // Add WAV output message callback // Saves samples to "out.wav" auto handler = rada2b->addMessageCallback( std::make_shared( "audio16bit.wav", /* output file name */ icsneo::ChannelMap( /** channel mapping which maps our output WAV channels to the message channels from incoming messages */ { /* See docs for specific A2B channel indexing information */ {static_cast(3u), static_cast(0u)}, /* Map output WAV channel 3 to channel 0 downstream of the A2B network/A2BMessage */ {static_cast(2u), static_cast(1u)}, /* Map output WAV channel 2 to channel 0 upstream of the A2B network/A2BMessage */ {static_cast(1u), static_cast(2u)}, /* Map output WAV channel 1 to channel 1 downstream of the A2B network/A2BMessage */ {static_cast(0u), static_cast(3u)} /* Map output WAV channel 0 to channel 1 upstream of the A2B network/A2BMessage */ } ), icsneo::PCMType::L16, /* store samples with 16 bit resolution*/ 2u, /* Number of channels in the output WAV file */ 48000 /* Sample rate of WAV file */ ) ); // Sleep this thread for 5 seconds, message callback still runs std::this_thread::sleep_for(std::chrono::seconds(5)); // Make sure you send 16 bit audio data on the above message channels in the channel map // to the RAD-A2B main node through a microphone or a different modem. // You can configure the message channels by changing the stream config in the A2B schematic // Remove callback rada2b->removeMessageCallback(handler); } /** * Example 2: RAD-A2B settings */ void example2(const std::shared_ptr& rada2b) { uint8_t numChannels; { // Get device settings auto* settings = rada2b->settings.get(); auto* rada2bSettings = static_cast(settings); // Check if monitor mode is enabled auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor); if(type == icsneo::RADA2BSettings::NodeType::Monitor) { std::cout << "Device is in monitor mode" << std::endl; } else { std::cout << "Device is not in monitor mode" << std::endl; } // Get current tdm mode numChannels = rada2bSettings->getNumChannels(icsneo::RADA2BSettings::RADA2BDevice::Node); std::cout << "Current num channels: " << static_cast(numChannels) << std::endl; // Set node type to master node. rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master); // Set TDM mode to TDM8 rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM4); // Apply local settings to device rada2bSettings->apply(); } } /** * Example 3: Packaging and transmitting sine tone using A2BMessage API */ void example3(const std::shared_ptr& rada2b) { std::cout << "Transmitting a 1000 hz sine tone." << std::endl; float deltaTime = static_cast(1.0/48000.0); float elapsedTime = 0.0; float twoPI = static_cast(2.0*atan(1.0)*4.0); float frequency = 1000; float amplitude = static_cast((1 << 15) - 1); size_t tdm = 4; size_t bytesPerSample = 2; size_t numFrames = 2048 / (2 * tdm * bytesPerSample); // Play for roughly 5 seconds while(elapsedTime < 5.0) { // Allocate message std::shared_ptr a2bmsgPtr = std::make_shared(numFrames, icsneo::A2BMessage::TDMMode::TDM4, true); icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get(); a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B_02); a2bmsg.txmsg = true; for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) { // Sine tone sample, amplitude 1000, frequency 1000 hz float contSample = amplitude*sin(twoPI*frequency*elapsedTime); icsneo::PCMSample sample = static_cast(contSample); // Send this sine wave sample downstream on channels 0, 1, and 2 a2bmsg.setChannelSample( icsneo::A2BMessage::Direction::Downstream, 0, frame, sample, icsneo::PCMType::L16 ); a2bmsg.setChannelSample( icsneo::A2BMessage::Direction::Downstream, 1, frame, sample, icsneo::PCMType::L16 ); a2bmsg.setChannelSample( icsneo::A2BMessage::Direction::Downstream, 2, frame, sample, icsneo::PCMType::L16 ); elapsedTime+=deltaTime; } // Transmit message to device if(!rada2b->transmit(a2bmsgPtr)) { std::cout << "Failed to transmit." << std::endl; } } } /** * Example 4: Retrieving A2B bus status using I2C messages. */ void example4(const std::shared_ptr& rada2b) { std::shared_ptr msg = std::make_shared(); std::shared_ptr msgFilter = std::make_shared(icsneo::Network::NetID::I2C_02); msg->network = icsneo::Network(icsneo::Network::NetID::I2C_02); msg->controlBytes.resize(1); msg->controlBytes[0] = static_cast(0x17u); // Register address for A2B INTTYPE msg->dataBytes.resize(1, 0); msg->direction = icsneo::I2CMessage::Direction::Read; msg->deviceMode = icsneo::I2CMessage::DeviceMode::Controller; msg->address = static_cast(0x68); // A2B master node address. msg->isTXMsg = true; auto handler = rada2b->addMessageCallback(std::make_shared( [] (std::shared_ptr newMsg) { if(newMsg->type == icsneo::Message::Type::Frame) { const auto& frame = std::dynamic_pointer_cast(newMsg); if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C_02) { const auto& i2cMessage = std::dynamic_pointer_cast(frame); if(!i2cMessage) { return; } if(i2cMessage->controlBytes.size() == 1 && i2cMessage->direction == icsneo::I2CMessage::Direction::Read) { if(i2cMessage->controlBytes[0] == 0x17u) { if(i2cMessage->dataBytes.size() == 1) { std::cout << "Current A2B bus status INTTYPE code: " << static_cast(i2cMessage->dataBytes[0]) << '\n'; } } else if(i2cMessage->controlBytes[0] == 0x03u) { if(i2cMessage->dataBytes.size() == 1) { std::cout << "A2B_PRODUCT register: " << static_cast(i2cMessage->dataBytes[0]) << std::endl; } } else if(i2cMessage->controlBytes[0] == 0x02u) { if(i2cMessage->dataBytes.size() == 1) { std::cout << "A2B_VENDOR register: " << static_cast(i2cMessage->dataBytes[0]) << std::endl; } } else if(i2cMessage->controlBytes[0] == 0x1C) { if(i2cMessage->dataBytes.size() == 1) { std::cout << "A2B_INTMSK1 register value: " << static_cast(i2cMessage->dataBytes[0]) << std::endl; } } } } } } , msgFilter)); if(!rada2b->transmit(msg)) { std::cout << "Failed to transmit." << std::endl; } msg->controlBytes[0] = 0x03; // Address for A2B_PRODUCT register if(!rada2b->transmit(msg)) { std::cout << "Failed to transmit." << std::endl; } msg->controlBytes[0] = 0x02; // Address for A2B_VENDOR register if(!rada2b->transmit(msg)) { std::cout << "Failed to transmit." << std::endl; } msg->controlBytes[0] = 0x1C ; // Address for A2B_INTMSK1 register msg->dataBytes[0] = 0x11; msg->direction = icsneo::I2CMessage::Direction::Write; // Write register if(!rada2b->transmit(msg)) { std::cout << "Failed to transmit" << std::endl; } std::this_thread::sleep_for(std::chrono::milliseconds(2000)); msg->direction = icsneo::I2CMessage::Direction::Read; // Read register if(!rada2b->transmit(msg)) { std::cout << "Failed to transmit." << std::endl; } std::this_thread::sleep_for(std::chrono::milliseconds(2000)); rada2b->removeMessageCallback(handler); } /** * Example 5: Reading A2B sequence chart .puml file */ void example5(const std::shared_ptr& rada2b) { // The A2B sequence chart is located at binary index 0 constexpr uint16_t a2bSequenceChartIndex = 0; // Create a ostream object to capture sequence chart data std::ofstream a2bSequenceChart("a2b_sequence_chart.puml", std::ios::out | std::ios::binary); if(!rada2b->readBinaryFile(a2bSequenceChart, a2bSequenceChartIndex)) { std::cout << "Failed to read A2B sequence chart" << std::endl; } } void displayUsage() { std::cout << "libicsneo A2B example" << std::endl; std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl; std::cout << "Options:" << std::endl; std::cout << "-h, --help\tDisplay help message." << std::endl; std::cout << "-e, --example [EXAMPLE_NUM]\tExample to run." << std::endl; std::cout << "Example usage: ./libicsneocpp-a2b.exe --example 1" << std::endl; std::cout << "Example usage: ./libicsneocpp-a2b.exe -h" << std::endl; std::cout << std::endl; std::cout << "Example options:" << std::endl; std::cout << "0\ttx" << std::endl; std::cout << "1\trx" << std::endl; std::cout << "2\tSet RAD-A2B settings" << std::endl; std::cout << "3\tPackaging and transmitting sine wav using A2BMessage API" << std::endl; std::cout << "4\tRead/write I2C registers on A2B board" << std::endl; std::cout << "5\tReading out A2B sequence chart .puml file" << std::endl; } int main(int argc, char** argv) { std::vector arguments(argv, argv + argc); if(argc > 4 || argc == 1) { std::cerr << "Invalid usage." << std::endl; displayUsage(); return EXIT_FAILURE; } if(std::any_of(arguments.begin(), arguments.end(), [](const std::string& arg) { return arg == "-h" || arg == "--help"; })) { displayUsage(); return EXIT_SUCCESS; } if(arguments[1] != "-e" && arguments[1] != "--example") { std::cerr << "Invalid usage." << std::endl; displayUsage(); return EXIT_FAILURE; } int option = atoi(arguments[2].c_str()); if(option < 0 || option > 5) { std::cerr << "Invalid usage." << std::endl; displayUsage(); return EXIT_FAILURE; } std::cout << icsneo::GetVersion() << std::endl; const auto& devices = icsneo::FindAllDevices(); auto it = std::find_if(devices.begin(), devices.end(), [&](const auto& dev) { const auto& txNetworks = dev->getSupportedTXNetworks(); const auto& rxNetworks = dev->getSupportedRXNetworks(); if(std::none_of(txNetworks.begin(), txNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) { return false; } if(std::none_of(rxNetworks.begin(), rxNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) { return false; } return true; } ); if(it == devices.end()) { std::cerr << "Could not find RAD-A2B." << std::endl; return EXIT_FAILURE; } std::shared_ptr rada2b = *it; if(!rada2b->open()) { std::cout << "Failed to open RAD-A2B." << std::endl; std::cout << icsneo::GetLastError() << std::endl; return EXIT_FAILURE; } else { std::cout << "Opened RAD-A2B." << std::endl; } if(!rada2b->goOnline()) { std::cout << "Failed to go online with RAD-A2B." << std::endl; std::cout << icsneo::GetLastError() << std::endl; return EXIT_FAILURE; } else { std::cout << "RAD-A2B online." << std::endl; } switch(option) { case 0: example0(rada2b); break; case 1: example1(rada2b); break; case 2: example2(rada2b); break; case 3: example3(rada2b); break; case 4: example4(rada2b); break; case 5: example5(rada2b); break; default: break; } rada2b->goOffline(); rada2b->close(); return EXIT_SUCCESS; }