<commit_before>#include "ffmpegdecoder.h"
#include "makeguard.h"
#include "interlockedadd.h"

#include <boost/log/trivial.hpp>

#include <functional>
#include <memory>
#include <tuple>

namespace {

uint8_t** getAudioData(AVFrame* audioFrame)
{
    return audioFrame->extended_data != nullptr
        ? audioFrame->extended_data
        : &audioFrame->data[0];
}

int64_t getChannelLayout(AVFrame* audioFrame)
{
	const int audioFrameChannels = audioFrame->channels; //av_frame_get_channels(audioFrame);
    return ((audioFrame->channel_layout != 0u) &&
        audioFrameChannels == av_get_channel_layout_nb_channels(audioFrame->channel_layout))
        ? audioFrame->channel_layout
        : av_get_default_channel_layout(audioFrameChannels);
}

} // namespace

void FFmpegDecoder::audioParseRunnable()
{
    CHANNEL_LOG(ffmpeg_threads) << "Audio thread started";
    AVPacket packet;

    bool initialized = false;
    bool failed = false;

    m_audioPlayer->InitializeThread();
    auto deinitializeThread = MakeGuard(
        m_audioPlayer.get(),
        std::mem_fn(&IAudioPlayer::DeinitializeThread));

    std::vector<uint8_t> resampleBuffer;

    while (!boost::this_thread::interruption_requested())
    {
        if (!m_audioPacketsQueue.pop(packet))
        {
            break;
        }

        auto packetGuard = MakeGuard(&packet, av_packet_unref);

        if (m_audioStreamNumber != packet.stream_index)
        {
            continue;
        }

        if (!initialized)
        {
            if (packet.pts != AV_NOPTS_VALUE)
            {
                const double pts = av_q2d(m_audioStream->time_base) * packet.pts;
                m_audioPTS = pts;
            }
            else
            {
                assert(false && "No audio pts found");
                return;
            }

            // invoke changedFramePosition() if needed
            //AppendFrameClock(0);
        }

        initialized = true;

        if (handleAudioPacket(packet, resampleBuffer, failed))
        {
            if (failed)
            {
                failed = false;
                boost::lock_guard<boost::mutex> locker(m_isPausedMutex);
                if (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED)
                {
                    m_audioPTS = (m_isPaused ? m_pauseTimer : GetHiResTime()) - m_videoStartClock;
                }
            }
        }
        else
        {
            failed = true;
        }
    }
}

bool FFmpegDecoder::handleAudioPacket(
    const AVPacket& packet,
    std::vector<uint8_t>& resampleBuffer,
    bool failed)
{
    if (packet.stream_index != m_audioStream->index)
    {
        avcodec_close(m_audioCodecContext);
        m_audioStream = m_formatContexts[m_audioContextIndex]->streams[packet.stream_index];
        if (!setupAudioCodec())
        {
            return false;
        }
    }

    const int ret = avcodec_send_packet(m_audioCodecContext, &packet);
    if (ret < 0) {
        return ret == AVERROR(EAGAIN) || ret == AVERROR_EOF;
    }

    AVFramePtr audioFrame(av_frame_alloc());
    bool result = true;
    while (avcodec_receive_frame(m_audioCodecContext, audioFrame.get()) == 0)
    {
        if (audioFrame->nb_samples <= 0)
        {
            continue;
        }

        const int original_buffer_size = av_samples_get_buffer_size(
            nullptr, audioFrame->channels,
            audioFrame->nb_samples,
            static_cast<AVSampleFormat>(audioFrame->format), 1);

        // write buffer
        uint8_t* write_data = *getAudioData(audioFrame.get());
        int64_t write_size = original_buffer_size;

        setupAudioSwrContext(audioFrame.get());

        if (m_audioSwrContext != nullptr)
        {
            enum { EXTRA_SPACE = 256 };

            const int out_count = static_cast<int64_t>(audioFrame->nb_samples) *
                m_audioSettings.frequency /
                m_audioCurrentPref.frequency + EXTRA_SPACE;

            const int size_multiplier = m_audioSettings.channels *
                av_get_bytes_per_sample(m_audioSettings.format);

            const size_t buffer_size = out_count * size_multiplier;

            if (resampleBuffer.size() < buffer_size)
            {
                resampleBuffer.resize(buffer_size);
            }

            // Code for resampling
            uint8_t *out = resampleBuffer.data();
            const int converted_size = swr_convert(
                m_audioSwrContext,
                &out,
                out_count,
                const_cast<const uint8_t**>(getAudioData(audioFrame.get())),
                audioFrame->nb_samples);

            if (converted_size < 0)
            {
                BOOST_LOG_TRIVIAL(error) << "swr_convert() failed";
                break;
            }

            if (converted_size == out_count)
            {
                BOOST_LOG_TRIVIAL(warning) << "audio buffer is probably too small";
                swr_init(m_audioSwrContext);
            }

            write_data = out;
            write_size = converted_size * size_multiplier;

            assert(write_size < buffer_size);
        }

        const double frame_clock
            = audioFrame->sample_rate != 0? double(audioFrame->nb_samples) / audioFrame->sample_rate : 0;

        bool skipAll = false;
        double delta = 0;
        bool isPaused = false;

        {
            boost::lock_guard<boost::mutex> locker(m_isPausedMutex);
            isPaused = m_isPaused;
            if (!isPaused)
            {
                delta = (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED)
                    ? GetHiResTime() - m_videoStartClock - m_audioPTS : 0;
            }
        }

        if (isPaused)
        {
            if (!m_audioPaused)
            {
                m_audioPlayer->WaveOutPause();
                m_audioPaused = true;
            }

            const bool hasVideo = m_mainVideoThread != nullptr;

            boost::unique_lock<boost::mutex> locker(m_isPausedMutex);

            while (m_isVideoSeekingWhilePaused && hasVideo
                || (delta = (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED)
                    ? (m_isPaused ? m_pauseTimer : GetHiResTime()) - m_videoStartClock - m_audioPTS : 0
                , m_isPaused && !(skipAll = delta >= frame_clock)))
            {
                m_isPausedCV.wait(locker);
            }
        }

        // Audio sync
        if (!failed && !skipAll && fabs(delta) > 0.1)
        {
            InterlockedAdd(m_videoStartClock, delta / 2);
        }

        if (m_audioPaused && !skipAll)
        {
            m_audioPlayer->WaveOutRestart();
            m_audioPaused = false;
        }

        if (boost::this_thread::interruption_requested())
        {
            return false;
        }

        if (skipAll)
        {
            InterlockedAdd(m_audioPTS, frame_clock);
        }
        else if (!(m_audioPlayer->WriteAudio(write_data, write_size)
            || initAudioOutput() && m_audioPlayer->WriteAudio(write_data, write_size)))
        {
            result = false;
        }
    }

    return result;
}

void FFmpegDecoder::setupAudioSwrContext(AVFrame* audioFrame)
{
    const auto audioFrameFormat = static_cast<AVSampleFormat>(audioFrame->format);
    const int audioFrameChannels = audioFrame->channels;

    const int64_t dec_channel_layout = getChannelLayout(audioFrame);

    const auto speed = getSpeedRational();

    // Check if the new swr context required
    if (audioFrameFormat != m_audioCurrentPref.format ||
        dec_channel_layout != m_audioCurrentPref.channel_layout ||
        (audioFrame->sample_rate * speed.numerator) / speed.denominator != m_audioCurrentPref.frequency)
    {
        swr_free(&m_audioSwrContext);
        m_audioSwrContext = swr_alloc_set_opts(
            nullptr, m_audioSettings.channel_layout, m_audioSettings.format,
            m_audioSettings.frequency * speed.denominator,
            dec_channel_layout, audioFrameFormat,
            audioFrame->sample_rate * speed.numerator, 0, nullptr);

        if ((m_audioSwrContext == nullptr) || swr_init(m_audioSwrContext) < 0)
        {
            BOOST_LOG_TRIVIAL(error) << "unable to initialize swr convert context";
        }

        m_audioCurrentPref.format = audioFrameFormat;
        m_audioCurrentPref.channels = audioFrameChannels;
        m_audioCurrentPref.channel_layout = dec_channel_layout;
        m_audioCurrentPref.frequency = (audioFrame->sample_rate * speed.numerator) / speed.denominator;
    }
}
<commit_msg>separate video/audio streams synchronization special case<commit_after>#include "ffmpegdecoder.h"
#include "makeguard.h"
#include "interlockedadd.h"

#include <boost/log/trivial.hpp>

#include <functional>
#include <memory>
#include <tuple>

namespace {

uint8_t** getAudioData(AVFrame* audioFrame)
{
    return audioFrame->extended_data != nullptr
        ? audioFrame->extended_data
        : &audioFrame->data[0];
}

int64_t getChannelLayout(AVFrame* audioFrame)
{
	const int audioFrameChannels = audioFrame->channels; //av_frame_get_channels(audioFrame);
    return ((audioFrame->channel_layout != 0u) &&
        audioFrameChannels == av_get_channel_layout_nb_channels(audioFrame->channel_layout))
        ? audioFrame->channel_layout
        : av_get_default_channel_layout(audioFrameChannels);
}

} // namespace

void FFmpegDecoder::audioParseRunnable()
{
    CHANNEL_LOG(ffmpeg_threads) << "Audio thread started";
    AVPacket packet;

    bool initialized = false;
    bool failed = false;

    m_audioPlayer->InitializeThread();
    auto deinitializeThread = MakeGuard(
        m_audioPlayer.get(),
        std::mem_fn(&IAudioPlayer::DeinitializeThread));

    std::vector<uint8_t> resampleBuffer;

    while (!boost::this_thread::interruption_requested())
    {
        if (!m_audioPacketsQueue.pop(packet))
        {
            break;
        }

        auto packetGuard = MakeGuard(&packet, av_packet_unref);

        if (m_audioStreamNumber != packet.stream_index)
        {
            continue;
        }

        if (!initialized)
        {
            if (packet.pts != AV_NOPTS_VALUE)
            {
                const double pts = av_q2d(m_audioStream->time_base) * packet.pts;
                m_audioPTS = pts;
            }
            else
            {
                assert(false && "No audio pts found");
                return;
            }

            // invoke changedFramePosition() if needed
            //AppendFrameClock(0);
        }

        initialized = true;

        if (handleAudioPacket(packet, resampleBuffer, failed))
        {
            if (failed)
            {
                failed = false;
                boost::lock_guard<boost::mutex> locker(m_isPausedMutex);
                if (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED)
                {
                    m_audioPTS = (m_isPaused ? m_pauseTimer : GetHiResTime()) - m_videoStartClock;
                }
            }
        }
        else
        {
            failed = true;
        }
    }
}

bool FFmpegDecoder::handleAudioPacket(
    const AVPacket& packet,
    std::vector<uint8_t>& resampleBuffer,
    bool failed)
{
    if (packet.stream_index != m_audioStream->index)
    {
        avcodec_close(m_audioCodecContext);
        m_audioStream = m_formatContexts[m_audioContextIndex]->streams[packet.stream_index];
        if (!setupAudioCodec())
        {
            return false;
        }
    }

    const int ret = avcodec_send_packet(m_audioCodecContext, &packet);
    if (ret < 0) {
        return ret == AVERROR(EAGAIN) || ret == AVERROR_EOF;
    }

    AVFramePtr audioFrame(av_frame_alloc());
    bool result = true;
    while (avcodec_receive_frame(m_audioCodecContext, audioFrame.get()) == 0)
    {
        if (audioFrame->nb_samples <= 0)
        {
            continue;
        }

        const int original_buffer_size = av_samples_get_buffer_size(
            nullptr, audioFrame->channels,
            audioFrame->nb_samples,
            static_cast<AVSampleFormat>(audioFrame->format), 1);

        // write buffer
        uint8_t* write_data = *getAudioData(audioFrame.get());
        int64_t write_size = original_buffer_size;

        setupAudioSwrContext(audioFrame.get());

        if (m_audioSwrContext != nullptr)
        {
            enum { EXTRA_SPACE = 256 };

            const int out_count = static_cast<int64_t>(audioFrame->nb_samples) *
                m_audioSettings.frequency /
                m_audioCurrentPref.frequency + EXTRA_SPACE;

            const int size_multiplier = m_audioSettings.channels *
                av_get_bytes_per_sample(m_audioSettings.format);

            const size_t buffer_size = out_count * size_multiplier;

            if (resampleBuffer.size() < buffer_size)
            {
                resampleBuffer.resize(buffer_size);
            }

            // Code for resampling
            uint8_t *out = resampleBuffer.data();
            const int converted_size = swr_convert(
                m_audioSwrContext,
                &out,
                out_count,
                const_cast<const uint8_t**>(getAudioData(audioFrame.get())),
                audioFrame->nb_samples);

            if (converted_size < 0)
            {
                BOOST_LOG_TRIVIAL(error) << "swr_convert() failed";
                break;
            }

            if (converted_size == out_count)
            {
                BOOST_LOG_TRIVIAL(warning) << "audio buffer is probably too small";
                swr_init(m_audioSwrContext);
            }

            write_data = out;
            write_size = converted_size * size_multiplier;

            assert(write_size < buffer_size);
        }

        const double frame_clock
            = audioFrame->sample_rate != 0? double(audioFrame->nb_samples) / audioFrame->sample_rate : 0;

        bool skipAll = false;
        double delta = 0;
        bool isPaused = false;

        {
            boost::lock_guard<boost::mutex> locker(m_isPausedMutex);
            isPaused = m_isPaused;
            if (!isPaused)
            {
                delta = (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED)
                    ? GetHiResTime() - m_videoStartClock - m_audioPTS : 0;
            }
        }

        if (isPaused)
        {
            if (!m_audioPaused)
            {
                m_audioPlayer->WaveOutPause();
                m_audioPaused = true;
            }

            const bool hasVideo = m_mainVideoThread != nullptr;

            boost::unique_lock<boost::mutex> locker(m_isPausedMutex);

            while (m_isVideoSeekingWhilePaused && hasVideo
                || (delta = (m_videoStartClock != VIDEO_START_CLOCK_NOT_INITIALIZED)
                    ? (m_isPaused ? m_pauseTimer : GetHiResTime()) - m_videoStartClock - m_audioPTS : 0
                , m_isPaused && !(skipAll = delta >= frame_clock)))
            {
                m_isPausedCV.wait(locker);
            }
        }
        else if (delta > 1 && m_formatContexts.size() > 1 && delta > frame_clock)
        {
            CHANNEL_LOG(ffmpeg_sync) << "Skip audio frame";
            skipAll = true;
        }

        // Audio sync
        if (!failed && !skipAll && fabs(delta) > 0.1)
        {
            InterlockedAdd(m_videoStartClock, delta / 2);
        }

        if (m_audioPaused && !skipAll)
        {
            m_audioPlayer->WaveOutRestart();
            m_audioPaused = false;
        }

        if (boost::this_thread::interruption_requested())
        {
            return false;
        }

        if (skipAll)
        {
            InterlockedAdd(m_audioPTS, frame_clock);
        }
        else if (!(m_audioPlayer->WriteAudio(write_data, write_size)
            || initAudioOutput() && m_audioPlayer->WriteAudio(write_data, write_size)))
        {
            result = false;
        }
    }

    return result;
}

void FFmpegDecoder::setupAudioSwrContext(AVFrame* audioFrame)
{
    const auto audioFrameFormat = static_cast<AVSampleFormat>(audioFrame->format);
    const int audioFrameChannels = audioFrame->channels;

    const int64_t dec_channel_layout = getChannelLayout(audioFrame);

    const auto speed = getSpeedRational();

    // Check if the new swr context required
    if (audioFrameFormat != m_audioCurrentPref.format ||
        dec_channel_layout != m_audioCurrentPref.channel_layout ||
        (audioFrame->sample_rate * speed.numerator) / speed.denominator != m_audioCurrentPref.frequency)
    {
        swr_free(&m_audioSwrContext);
        m_audioSwrContext = swr_alloc_set_opts(
            nullptr, m_audioSettings.channel_layout, m_audioSettings.format,
            m_audioSettings.frequency * speed.denominator,
            dec_channel_layout, audioFrameFormat,
            audioFrame->sample_rate * speed.numerator, 0, nullptr);

        if ((m_audioSwrContext == nullptr) || swr_init(m_audioSwrContext) < 0)
        {
            BOOST_LOG_TRIVIAL(error) << "unable to initialize swr convert context";
        }

        m_audioCurrentPref.format = audioFrameFormat;
        m_audioCurrentPref.channels = audioFrameChannels;
        m_audioCurrentPref.channel_layout = dec_channel_layout;
        m_audioCurrentPref.frequency = (audioFrame->sample_rate * speed.numerator) / speed.denominator;
    }
}
<|endoftext|>


<commit_before>#include "ffmpegdecoder.h"
#include "makeguard.h"
#include "interlockedadd.h"

#include <boost/log/trivial.hpp>

namespace {

bool frameToImage(
    VideoFrame& videoFrameData,
    AVFrame*& m_videoFrame,
    SwsContext*& m_imageCovertContext,
    AVPixelFormat m_pixelFormat)
{
    if (m_videoFrame->format == m_pixelFormat
        || m_videoFrame->format == AV_PIX_FMT_DXVA2_VLD)
    {
        std::swap(m_videoFrame, videoFrameData.m_image);
    }
    else
    {
        const int width = m_videoFrame->width;
        const int height = m_videoFrame->height;

        videoFrameData.realloc(m_pixelFormat, width, height);

        // Prepare image conversion
        m_imageCovertContext =
            sws_getCachedContext(m_imageCovertContext, m_videoFrame->width, m_videoFrame->height,
            (AVPixelFormat)m_videoFrame->format, width, height, m_pixelFormat,
            0, nullptr, nullptr, nullptr);

        assert(m_imageCovertContext != nullptr);

        if (m_imageCovertContext == nullptr)
        {
            return false;
        }

        // Doing conversion
        if (sws_scale(m_imageCovertContext, m_videoFrame->data, m_videoFrame->linesize, 0,
            m_videoFrame->height, videoFrameData.m_image->data, videoFrameData.m_image->linesize) <= 0)
        {
            assert(false && "sws_scale failed");
            BOOST_LOG_TRIVIAL(error) << "sws_scale failed";
            return false;
        }

        videoFrameData.m_image->sample_aspect_ratio = m_videoFrame->sample_aspect_ratio;
    }

    return true;
}

} // namespace

void FFmpegDecoder::videoParseRunnable()
{
    CHANNEL_LOG(ffmpeg_threads) << "Video thread started";
    m_videoStartClock = GetHiResTime();
    double videoClock = 0; // pts of last decoded frame / predicted pts of next decoded frame

    bool initialized = false;

    for (;;)
    {
        if (m_isPaused && !m_isVideoSeekingWhilePaused)
        {
            boost::unique_lock<boost::mutex> locker(m_isPausedMutex);
            while (m_isPaused)
            {
                m_isPausedCV.wait(locker);
            }
        }

        for (;;)
        {
            AVPacket packet;
            if (!m_videoPacketsQueue.pop(packet,
                [this] { return m_isPaused && !m_isVideoSeekingWhilePaused; }))
            {
                break;
            }

            auto packetGuard = MakeGuard(&packet, av_packet_unref);

            if (!handleVideoPacket(packet, videoClock, initialized)
                || m_isPaused && !m_isVideoSeekingWhilePaused)
            {
                break;
            }
        }
    }
}

bool FFmpegDecoder::handleVideoPacket(
    const AVPacket& packet,
    double& videoClock,
    bool& initialized)
{
    const int ret = avcodec_send_packet(m_videoCodecContext, &packet);
    if (ret < 0)
        return false;

    while (avcodec_receive_frame(m_videoCodecContext, m_videoFrame) == 0)
    {
        const int64_t duration_stamp =
            av_frame_get_best_effort_timestamp(m_videoFrame);

        // compute the exact PTS for the picture if it is omitted in the stream
        // pts1 is the dts of the pkt / pts of the frame
        if (duration_stamp != AV_NOPTS_VALUE)
        {
            videoClock = duration_stamp * av_q2d(m_videoStream->time_base);
        }
        const double pts = videoClock;

        if (!initialized)
        {
            m_videoStartClock = GetHiResTime() - pts;
        }

        // update video clock for next frame
        // for MPEG2, the frame can be repeated, so we update the clock accordingly
        const double frameDelay = av_q2d(m_videoCodecContext->time_base) *
            (1. + m_videoFrame->repeat_pict * 0.5);
        videoClock += frameDelay;

        boost::posix_time::time_duration td(boost::posix_time::pos_infin);
        // Skipping frames
        if (initialized)
        {
            double curTime = GetHiResTime();
            if (m_videoStartClock + pts <= curTime)
            {
                if (m_videoStartClock + pts < curTime - 1.)
                {
                    InterlockedAdd(m_videoStartClock, 1.);
                }

                CHANNEL_LOG(ffmpeg_sync) << "Hard skip frame";

                // pause
                if (m_isPaused && !m_isVideoSeekingWhilePaused)
                {
                    break;
                }

                continue;
            }

            td = boost::posix_time::milliseconds(
                int((m_videoStartClock + pts - curTime) * 1000.) + 1);
        }

        initialized = true;

        {
            boost::unique_lock<boost::mutex> locker(m_videoFramesMutex);

            if (!m_videoFramesCV.timed_wait(locker, td, [this]
                {
                    return m_isPaused && !m_isVideoSeekingWhilePaused ||
                        m_videoFramesQueue.canPush();
                }))
            {
                continue;
            }
        }

        if (m_isPaused && !m_isVideoSeekingWhilePaused)
        {
            break;
        }

        m_isVideoSeekingWhilePaused = false;

        VideoFrame& current_frame = m_videoFramesQueue.back();
        handleDirect3dData(m_videoFrame);
        if (!frameToImage(current_frame, m_videoFrame, m_imageCovertContext, m_pixelFormat))
        {
            continue;
        }

        current_frame.m_pts = pts;
        current_frame.m_duration = duration_stamp;

        {
            boost::lock_guard<boost::mutex> locker(m_videoFramesMutex);
            m_videoFramesQueue.pushBack();
        }
        m_videoFramesCV.notify_all();
    }

    return true;
}
<commit_msg>fix for slow connections<commit_after>#include "ffmpegdecoder.h"
#include "makeguard.h"
#include "interlockedadd.h"

#include <boost/log/trivial.hpp>

namespace {

bool frameToImage(
    VideoFrame& videoFrameData,
    AVFrame*& m_videoFrame,
    SwsContext*& m_imageCovertContext,
    AVPixelFormat m_pixelFormat)
{
    if (m_videoFrame->format == m_pixelFormat
        || m_videoFrame->format == AV_PIX_FMT_DXVA2_VLD)
    {
        std::swap(m_videoFrame, videoFrameData.m_image);
    }
    else
    {
        const int width = m_videoFrame->width;
        const int height = m_videoFrame->height;

        videoFrameData.realloc(m_pixelFormat, width, height);

        // Prepare image conversion
        m_imageCovertContext =
            sws_getCachedContext(m_imageCovertContext, m_videoFrame->width, m_videoFrame->height,
            (AVPixelFormat)m_videoFrame->format, width, height, m_pixelFormat,
            0, nullptr, nullptr, nullptr);

        assert(m_imageCovertContext != nullptr);

        if (m_imageCovertContext == nullptr)
        {
            return false;
        }

        // Doing conversion
        if (sws_scale(m_imageCovertContext, m_videoFrame->data, m_videoFrame->linesize, 0,
            m_videoFrame->height, videoFrameData.m_image->data, videoFrameData.m_image->linesize) <= 0)
        {
            assert(false && "sws_scale failed");
            BOOST_LOG_TRIVIAL(error) << "sws_scale failed";
            return false;
        }

        videoFrameData.m_image->sample_aspect_ratio = m_videoFrame->sample_aspect_ratio;
    }

    return true;
}

} // namespace

void FFmpegDecoder::videoParseRunnable()
{
    CHANNEL_LOG(ffmpeg_threads) << "Video thread started";
    m_videoStartClock = GetHiResTime();
    double videoClock = 0; // pts of last decoded frame / predicted pts of next decoded frame

    bool initialized = false;

    for (;;)
    {
        if (m_isPaused && !m_isVideoSeekingWhilePaused)
        {
            boost::unique_lock<boost::mutex> locker(m_isPausedMutex);
            while (m_isPaused)
            {
                m_isPausedCV.wait(locker);
            }
        }

        for (;;)
        {
            AVPacket packet;
            if (!m_videoPacketsQueue.pop(packet,
                [this] { return m_isPaused && !m_isVideoSeekingWhilePaused; }))
            {
                break;
            }

            auto packetGuard = MakeGuard(&packet, av_packet_unref);

            if (!handleVideoPacket(packet, videoClock, initialized)
                || m_isPaused && !m_isVideoSeekingWhilePaused)
            {
                break;
            }
        }
    }
}

bool FFmpegDecoder::handleVideoPacket(
    const AVPacket& packet,
    double& videoClock,
    bool& initialized)
{
    const int ret = avcodec_send_packet(m_videoCodecContext, &packet);
    if (ret < 0)
        return false;

    while (avcodec_receive_frame(m_videoCodecContext, m_videoFrame) == 0)
    {
        const int64_t duration_stamp =
            av_frame_get_best_effort_timestamp(m_videoFrame);

        // compute the exact PTS for the picture if it is omitted in the stream
        // pts1 is the dts of the pkt / pts of the frame
        if (duration_stamp != AV_NOPTS_VALUE)
        {
            videoClock = duration_stamp * av_q2d(m_videoStream->time_base);
        }
        const double pts = videoClock;

        if (!initialized)
        {
            m_videoStartClock = GetHiResTime() - pts;
        }

        // update video clock for next frame
        // for MPEG2, the frame can be repeated, so we update the clock accordingly
        const double frameDelay = av_q2d(m_videoCodecContext->time_base) *
            (1. + m_videoFrame->repeat_pict * 0.5);
        videoClock += frameDelay;

        boost::posix_time::time_duration td(boost::posix_time::pos_infin);
        // Skipping frames
        if (initialized && !m_videoPacketsQueue.empty())
        {
            const double curTime = GetHiResTime();
            if (m_videoStartClock + pts <= curTime)
            {
                if (m_videoStartClock + pts < curTime - 1.)
                {
                    InterlockedAdd(m_videoStartClock, 1.);
                }

                CHANNEL_LOG(ffmpeg_sync) << "Hard skip frame";

                // pause
                if (m_isPaused && !m_isVideoSeekingWhilePaused)
                {
                    break;
                }

                continue;
            }

            td = boost::posix_time::milliseconds(
                int((m_videoStartClock + pts - curTime) * 1000.) + 1);
        }

        initialized = true;

        {
            boost::unique_lock<boost::mutex> locker(m_videoFramesMutex);

            if (!m_videoFramesCV.timed_wait(locker, td, [this]
                {
                    return m_isPaused && !m_isVideoSeekingWhilePaused ||
                        m_videoFramesQueue.canPush();
                }))
            {
                continue;
            }
        }

        if (m_isPaused && !m_isVideoSeekingWhilePaused)
        {
            break;
        }

        m_isVideoSeekingWhilePaused = false;

        VideoFrame& current_frame = m_videoFramesQueue.back();
        handleDirect3dData(m_videoFrame);
        if (!frameToImage(current_frame, m_videoFrame, m_imageCovertContext, m_pixelFormat))
        {
            continue;
        }

        current_frame.m_pts = pts;
        current_frame.m_duration = duration_stamp;

        {
            boost::lock_guard<boost::mutex> locker(m_videoFramesMutex);
            m_videoFramesQueue.pushBack();
        }
        m_videoFramesCV.notify_all();
    }

    return true;
}
<|endoftext|>


<commit_before>// -------------------------------------------------------------------
// @author Toby D. Young
//
// Copyright 2014 nil authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
//    copyright notice, this list of conditions and the following
//    disclaimer in the documentation and/or other materials provided
//    with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE NAMEPSACE EWALENA AUTHORS ``AS
// IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// NAMESPACE EWALENA AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and
// documentation are those of the authors and should not be
// interpreted as representing official policies, either expressed or
// implied, of the namespace ewalena authors.
// -------------------------------------------------------------------

// deal.II headers
#include <deal.II/base/conditional_ostream.h>
#include <deal.II/base/index_set.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/numbers.h>
#include <deal.II/base/parameter_handler.h>
#include <deal.II/base/tensor.h>
#include <deal.II/base/table_handler.h>

#include <deal.II/distributed/tria.h>
#include <deal.II/distributed/grid_refinement.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/fe/mapping_q.h>
#include <deal.II/fe/fe_q.h>

#include <deal.II/grid/grid_generator.h>

#include <deal.II/lac/compressed_simple_sparsity_pattern.h>
#include <deal.II/lac/petsc_parallel_sparse_matrix.h>
#include <deal.II/lac/petsc_parallel_vector.h>
#include <deal.II/lac/sparsity_tools.h>

// Library-based headers.
#include "group_symmetry.h"
#include "piezoelectric_tensor.h"

// Application-base headers.
#include "command_line.h"

#include <fstream>
#include <iostream>

/**
 * This class can setup, solve, and output the results of the
 * piezoelectric problem.
 *
 * @author Toby D. Young 2012, 2014
 */
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType = double>
class PiezoelectricProblem
{
public:

  /**
   * Constructor.
   */
  PiezoelectricProblem ();

  /**
   * destructor.
   */
  ~PiezoelectricProblem ();

  /**
   * Run the problem (a basic useage example)
   */
  void run ();

  // Coefficients are held in a parameter file, so it will be
  // necessary to read in a file from the command line.
  nil::CommandLine command_line;

private:

  // First is a list of functions that belong to this class (they are
  // explained later on).
  void get_parameters ();

  void make_coarse_grid (const unsigned int n_refinement_cycles = 0);
  void make_boundary_constraints ();

  void setup_system ();
  void setup_coefficients ();

  void output_results () const;

  /**
   * A local copy of the MPI communicator.
   */
  MPI_Comm mpi_communicator;

  // Following that we have a list of the tensors that will be used in
  // this calculation. They are, first- and second-order piezoelectric
  // tensors
  nil::PiezoelectricTensor<GroupSymm, 1, ValueType> first_order_piezoelectric_tensor;
  nil::PiezoelectricTensor<GroupSymm, 2, ValueType> second_order_piezoelectric_tensor;

  // Additionally, lists of coefficients are needed for those tensors
  // that are tensors of empirical moduli
  std::vector<ValueType> first_order_piezoelectric_coefficients;
  std::vector<ValueType> second_order_piezoelectric_coefficients;

  dealii::ConditionalOStream pcout;

  /**
   * A parallel distributed triangulation.
   */
  dealii::parallel::distributed::Triangulation<dim> triangulation;

  // piezoelectric problem
  const dealii::FESystem<dim>                    fe_q;
  dealii::DoFHandler<dim>                        dof_handler;
  dealii::ConstraintMatrix                       constraints;

  /**
   * An index set of the degrees of freedom locally owned by this
   * processor.
   */
  dealii::IndexSet                               locally_owned_dofs;

  /**
   * An index set of the degrees of freedom?
   */
  dealii::IndexSet                               locally_relevant_dofs;

  /**
   * The system matrix. This has a block structure but, since we do
   * not need the blocks, us a standard MPI sparse matrix.
   */
  dealii::PETScWrappers::MPI::SparseMatrix       system_matrix;

  /**
   * The system rhs. This has a block structure but, since we do not
   * need the blocks, us a standard MPI vector.
   */
  dealii::PETScWrappers::MPI::Vector             system_rhs;

  /**
   * The solution. This has a block structure but, since we do not
   * need the blocks, us a standard MPI vector. @note This vector has
   * hpghost elements.
   */
  dealii::PETScWrappers::MPI::Vector             solution;

  // Then we need an object to hold various run-time parameters that
  // are specified in an "prm file".
  dealii::ParameterHandler parameters;

  // and finally, a table to handle the results.
  dealii::TableHandler output_table;
};

/**
 * Constructor of the piezoelectric problem
 */
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
PiezoelectricProblem<dim, GroupSymm, ValueType>::PiezoelectricProblem ()
  :
  mpi_communicator (MPI_COMM_WORLD),

  pcout (std::cout, (dealii::Utilities::MPI::this_mpi_process (MPI_COMM_WORLD) == 0)),

  triangulation (MPI_COMM_WORLD,
                   typename dealii::Triangulation<dim>::MeshSmoothing
                   (dealii::Triangulation<dim>::smoothing_on_refinement |
		    dealii::Triangulation<dim>::smoothing_on_coarsening)),

  fe_q (dealii::FE_Q<dim> (2), dim), /* displacement       */
	// dealii::FE_Q<dim> (1), 1),  /* electric potential */

  dof_handler (triangulation)
{}

// as is the destructor.
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
PiezoelectricProblem<dim, GroupSymm, ValueType>::~PiezoelectricProblem ()
{
  dof_handler.clear ();
}

// The first step is to obtain a complete list of the coefficients
// needed for this calculation. First comes a declaration of the
// entries expected to be find in the parameter file and then they are
// read into the object parameters.
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::get_parameters ()
{
  // First declare the parameters that are expected to be found.
  parameters.declare_entry ("First-order piezoelectric coefficients",
			    "-0.230",
			    dealii::Patterns::List (dealii::Patterns::Double (), 1),
			    "A list of the first-order piezoelectric coefficients. "
			    "Default is zinc-blende GaAs. "
			    "See: PRL 96, 187602 (2006). ");

  parameters.declare_entry ("Second-order piezoelectric coefficients",
			    "-0.439, -3.765, -0.492",
			    dealii::Patterns::List (dealii::Patterns::Double (), 1),
			    "A list of the second-order piezoelectric coefficients. "
			    "Default is zinc-blende GaAs. "
			    "See: PRL 96, 187602 (2006). ");

  // parameters.declare_entry ("Bravais lattice dimensions",
  // 			    "1., 1., 1.",
  // 			    dealii::Patterns::List (dealii::Patterns::Double (), 1),
  // 			    "A list of the Bravais lattice dimensions. ");

  parameters.read_input (command_line.get_prm_file ());

  first_order_piezoelectric_coefficients =
    dealii::Utilities::string_to_double
    (dealii::Utilities::split_string_list (parameters.get ("First-order piezoelectric coefficients")));

  second_order_piezoelectric_coefficients =
    dealii::Utilities::string_to_double
    (dealii::Utilities::split_string_list (parameters.get ("Second-order piezoelectric coefficients")));

  // bravais_lattice =
  //   dealii::Utilities::string_to_double
  //   (dealii::Utilities::split_string_list (parameters.get ("Bravais lattice dimensions")));

  // Some checks
  // AssertThrow (bravais_lattice.size ()==3,
  // 	       dealii::ExcMessage ("The Bravais lattice is required to have three components."));

}

template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::make_coarse_grid (const unsigned int n_refinement_cycles)
{
  dealii::GridGenerator::hyper_rectangle (triangulation,
					  dealii::Point<dim> (-20,-20,-20),
					  dealii::Point<dim> ( 20, 20, 20),
					  true);

  triangulation.refine_global (n_refinement_cycles);
}

// Next initialise all of the objects we are going to use.
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::setup_coefficients ()
{
  // Initialise the first- and second-order piezoelectric tensors...
  first_order_piezoelectric_tensor.reinit ();
  second_order_piezoelectric_tensor.reinit ();

  // and distribute the coefficients.
  first_order_piezoelectric_tensor.distribute_coefficients (first_order_piezoelectric_coefficients);
  second_order_piezoelectric_tensor.distribute_coefficients (second_order_piezoelectric_coefficients);
}

template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::setup_system ()
{
  dof_handler.distribute_dofs (fe_q);

  locally_owned_dofs = dof_handler.locally_owned_dofs ();
  dealii::DoFTools::extract_locally_relevant_dofs (dof_handler,
						   locally_relevant_dofs);

  constraints.clear ();
  constraints.reinit (locally_relevant_dofs);
  dealii::DoFTools::make_hanging_node_constraints (dof_handler, constraints);
  dealii::DoFTools::make_zero_boundary_constraints (dof_handler, constraints);
  constraints.close ();

  dealii::CompressedSimpleSparsityPattern csp (locally_relevant_dofs);
  dealii::DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
  dealii::SparsityTools::distribute_sparsity_pattern (csp,
						      dof_handler.n_locally_owned_dofs_per_processor (),
						      mpi_communicator,
						      locally_relevant_dofs);

  // @todo The system_matrix initialisation screws up with petsc-3.5.x
  system_matrix.reinit (locally_owned_dofs,
			locally_owned_dofs,
			csp,
			mpi_communicator);

  system_rhs.reinit (locally_owned_dofs, mpi_communicator);

  // The solution vector has ghost elements
  solution.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);

}

/**
 * This is the run function, which wraps all of the above into a
 * single logical routine.
 */
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::run ()
{

  // First find the parameters need for this calculation
  get_parameters ();

  // Then create the coarse grid
  make_coarse_grid (3);

  // Here comes the adaptive cycles
  for (unsigned int cycle=0; cycle<1; ++cycle)
    {

      pcout << "   Number of active cells:       "
	    << triangulation.n_global_active_cells ()
	    << " (on "
	    << triangulation.n_levels ()
	    << " levels)"
	    << std::endl;

      setup_system ();

      pcout << "   Number of degrees of freedom: "
	    << dof_handler.n_dofs ()
	    << std::endl;

    }

}

int main (int argc, char **argv)
{

  // Initialise MPI as we allways do.
  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, dealii::numbers::invalid_unsigned_int);

  try
    {
      dealii::deallog.depth_console (0);

      // Initialise the problem, 3d wurtzite, parse the command
      // line options and then run the problem.
      PiezoelectricProblem<3, nil::GroupSymmetry::Wurtzite> piezoelectric_problem;
      piezoelectric_problem.command_line.parse_command_line (argc, argv);
      piezoelectric_problem.run ();
    }

  // ...and if this should fail, try to gather as much information as
  // possible. Specifically, if the exception that was thrown is an
  // object of a class that is derived from the C++ standard class
  // exception.
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }

  // If the exception that was thrown somewhere was not an object of a
  // class derived from the standard exception class,
  // then nothing cane be done at all - simply print an error message
  // and exit.
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  // At this point, the application performed as was expected - return
  // without error.
  return 0;
}
<commit_msg>Get ready to initialise tensors.<commit_after>// -------------------------------------------------------------------
// @author Toby D. Young
//
// Copyright 2014 nil authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
//    copyright notice, this list of conditions and the following
//    disclaimer in the documentation and/or other materials provided
//    with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE NAMEPSACE EWALENA AUTHORS ``AS
// IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// NAMESPACE EWALENA AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and
// documentation are those of the authors and should not be
// interpreted as representing official policies, either expressed or
// implied, of the namespace ewalena authors.
// -------------------------------------------------------------------

// deal.II headers
#include <deal.II/base/conditional_ostream.h>
#include <deal.II/base/index_set.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/numbers.h>
#include <deal.II/base/parameter_handler.h>
#include <deal.II/base/tensor.h>
#include <deal.II/base/table_handler.h>

#include <deal.II/distributed/tria.h>
#include <deal.II/distributed/grid_refinement.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/fe/mapping_q.h>
#include <deal.II/fe/fe_q.h>

#include <deal.II/grid/grid_generator.h>

#include <deal.II/lac/compressed_simple_sparsity_pattern.h>
#include <deal.II/lac/petsc_parallel_sparse_matrix.h>
#include <deal.II/lac/petsc_parallel_vector.h>
#include <deal.II/lac/sparsity_tools.h>

// Library-based headers.
#include "group_symmetry.h"
#include "dielectric_tensor.h"
#include "elastic_tensor.h"
#include "piezoelectric_tensor.h"

// Application-base headers.
#include "command_line.h"

#include <fstream>
#include <iostream>

/**
 * This class can setup, solve, and output the results of the
 * piezoelectric problem.
 *
 * @author Toby D. Young 2012, 2014
 */
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType = double>
class PiezoelectricProblem
{
public:

  /**
   * Constructor.
   */
  PiezoelectricProblem ();

  /**
   * destructor.
   */
  ~PiezoelectricProblem ();

  /**
   * Run the problem (a basic useage example)
   */
  void run ();

  // Coefficients are held in a parameter file, so it will be
  // necessary to read in a file from the command line.
  nil::CommandLine command_line;

private:

  // First is a list of functions that belong to this class (they are
  // explained later on).
  void get_parameters ();

  void make_coarse_grid (const unsigned int n_refinement_cycles = 0);
  void make_boundary_constraints ();

  void setup_system ();
  void setup_coefficients ();

  void output_results () const;

  /**
   * A local copy of the MPI communicator.
   */
  MPI_Comm mpi_communicator;

  // Following that we have a list of the tensors that will be used in
  // this calculation. They are, first- and second-order piezoelectric
  // tensors
  nil::ElasticTensor<GroupSymm, 1, ValueType>       first_order_elastic_tensor;
  nil::DielectricTensor<GroupSymm, 1, ValueType>    first_order_dielectric_tensor;
  nil::PiezoelectricTensor<GroupSymm, 1, ValueType> first_order_piezoelectric_tensor;
  nil::PiezoelectricTensor<GroupSymm, 2, ValueType> second_order_piezoelectric_tensor;

  // Additionally, lists of coefficients are needed for those tensors
  // that are tensors of empirical moduli
  std::vector<ValueType> first_order_elastic_coefficients;
  std::vector<ValueType> first_order_dielectric_coefficients;
  std::vector<ValueType> first_order_piezoelectric_coefficients;
  std::vector<ValueType> second_order_piezoelectric_coefficients;

  dealii::ConditionalOStream pcout;

  /**
   * A parallel distributed triangulation.
   */
  dealii::parallel::distributed::Triangulation<dim> triangulation;

  // piezoelectric problem
  const dealii::FESystem<dim>                    fe_q;
  dealii::DoFHandler<dim>                        dof_handler;
  dealii::ConstraintMatrix                       constraints;

  /**
   * An index set of the degrees of freedom locally owned by this
   * processor.
   */
  dealii::IndexSet                               locally_owned_dofs;

  /**
   * An index set of the degrees of freedom?
   */
  dealii::IndexSet                               locally_relevant_dofs;

  /**
   * The system matrix. This has a block structure but, since we do
   * not need the blocks, us a standard MPI sparse matrix.
   */
  dealii::PETScWrappers::MPI::SparseMatrix       system_matrix;

  /**
   * The system rhs. This has a block structure but, since we do not
   * need the blocks, us a standard MPI vector.
   */
  dealii::PETScWrappers::MPI::Vector             system_rhs;

  /**
   * The solution. This has a block structure but, since we do not
   * need the blocks, us a standard MPI vector. @note This vector has
   * hpghost elements.
   */
  dealii::PETScWrappers::MPI::Vector             solution;

  // Then we need an object to hold various run-time parameters that
  // are specified in an "prm file".
  dealii::ParameterHandler parameters;

  // and finally, a table to handle the results.
  dealii::TableHandler output_table;
};

/**
 * Constructor of the piezoelectric problem
 */
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
PiezoelectricProblem<dim, GroupSymm, ValueType>::PiezoelectricProblem ()
  :
  mpi_communicator (MPI_COMM_WORLD),

  pcout (std::cout, (dealii::Utilities::MPI::this_mpi_process (MPI_COMM_WORLD) == 0)),

  triangulation (MPI_COMM_WORLD,
                   typename dealii::Triangulation<dim>::MeshSmoothing
                   (dealii::Triangulation<dim>::smoothing_on_refinement |
		    dealii::Triangulation<dim>::smoothing_on_coarsening)),

  fe_q (dealii::FE_Q<dim> (2), dim), /* displacement       */
	// dealii::FE_Q<dim> (1), 1),  /* electric potential */

  dof_handler (triangulation)
{}

// as is the destructor.
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
PiezoelectricProblem<dim, GroupSymm, ValueType>::~PiezoelectricProblem ()
{
  dof_handler.clear ();
}

// The first step is to obtain a complete list of the coefficients
// needed for this calculation. First comes a declaration of the
// entries expected to be find in the parameter file and then they are
// read into the object parameters.
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::get_parameters ()
{
  // First declare the parameters that are expected to be found.
  parameters.declare_entry ("First-order piezoelectric coefficients",
			    "-0.230",
			    dealii::Patterns::List (dealii::Patterns::Double (), 1),
			    "A list of the first-order piezoelectric coefficients. "
			    "Default is zinc-blende GaAs. "
			    "See: PRL 96, 187602 (2006). ");

  parameters.declare_entry ("Second-order piezoelectric coefficients",
			    "-0.439, -3.765, -0.492",
			    dealii::Patterns::List (dealii::Patterns::Double (), 1),
			    "A list of the second-order piezoelectric coefficients. "
			    "Default is zinc-blende GaAs. "
			    "See: PRL 96, 187602 (2006). ");

  // parameters.declare_entry ("Bravais lattice dimensions",
  // 			    "1., 1., 1.",
  // 			    dealii::Patterns::List (dealii::Patterns::Double (), 1),
  // 			    "A list of the Bravais lattice dimensions. ");

  parameters.read_input (command_line.get_prm_file ());

  first_order_piezoelectric_coefficients =
    dealii::Utilities::string_to_double
    (dealii::Utilities::split_string_list (parameters.get ("First-order piezoelectric coefficients")));

  second_order_piezoelectric_coefficients =
    dealii::Utilities::string_to_double
    (dealii::Utilities::split_string_list (parameters.get ("Second-order piezoelectric coefficients")));

  // bravais_lattice =
  //   dealii::Utilities::string_to_double
  //   (dealii::Utilities::split_string_list (parameters.get ("Bravais lattice dimensions")));

  // Some checks
  // AssertThrow (bravais_lattice.size ()==3,
  // 	       dealii::ExcMessage ("The Bravais lattice is required to have three components."));

}

template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::make_coarse_grid (const unsigned int n_refinement_cycles)
{
  dealii::GridGenerator::hyper_rectangle (triangulation,
					  dealii::Point<dim> (-20,-20,-20),
					  dealii::Point<dim> ( 20, 20, 20),
					  true);

  triangulation.refine_global (n_refinement_cycles);
}

// Next initialise all of the objects we are going to use.
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::setup_coefficients ()
{
  // Initialise the first- and second-order piezoelectric tensors...
  first_order_piezoelectric_tensor.reinit ();
  second_order_piezoelectric_tensor.reinit ();

  // and distribute the coefficients.
  first_order_piezoelectric_tensor.distribute_coefficients (first_order_piezoelectric_coefficients);
  second_order_piezoelectric_tensor.distribute_coefficients (second_order_piezoelectric_coefficients);
}

template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::setup_system ()
{
  dof_handler.distribute_dofs (fe_q);

  locally_owned_dofs = dof_handler.locally_owned_dofs ();
  dealii::DoFTools::extract_locally_relevant_dofs (dof_handler,
						   locally_relevant_dofs);

  constraints.clear ();
  constraints.reinit (locally_relevant_dofs);
  dealii::DoFTools::make_hanging_node_constraints (dof_handler, constraints);
  dealii::DoFTools::make_zero_boundary_constraints (dof_handler, constraints);
  constraints.close ();

  dealii::CompressedSimpleSparsityPattern csp (locally_relevant_dofs);
  dealii::DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
  dealii::SparsityTools::distribute_sparsity_pattern (csp,
						      dof_handler.n_locally_owned_dofs_per_processor (),
						      mpi_communicator,
						      locally_relevant_dofs);

  // @todo The system_matrix initialisation screws up with petsc-3.5.x
  system_matrix.reinit (locally_owned_dofs,
			locally_owned_dofs,
			csp,
			mpi_communicator);

  system_rhs.reinit (locally_owned_dofs, mpi_communicator);

  // The solution vector has ghost elements
  solution.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);

}

/**
 * This is the run function, which wraps all of the above into a
 * single logical routine.
 */
template <int dim, enum nil::GroupSymmetry GroupSymm, typename ValueType>
void
PiezoelectricProblem<dim, GroupSymm, ValueType>::run ()
{

  // First find the parameters need for this calculation
  get_parameters ();

  // Then create the coarse grid
  make_coarse_grid (3);

  // Here comes the adaptive cycles
  for (unsigned int cycle=0; cycle<1; ++cycle)
    {

      pcout << "   Number of active cells:       "
	    << triangulation.n_global_active_cells ()
	    << " (on "
	    << triangulation.n_levels ()
	    << " levels)"
	    << std::endl;

      setup_system ();

      pcout << "   Number of degrees of freedom: "
	    << dof_handler.n_dofs ()
	    << std::endl;

    }

}

int main (int argc, char **argv)
{

  // Initialise MPI as we allways do.
  dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, dealii::numbers::invalid_unsigned_int);

  try
    {
      dealii::deallog.depth_console (0);

      // Initialise the problem, 3d wurtzite, parse the command
      // line options and then run the problem.
      PiezoelectricProblem<3, nil::GroupSymmetry::Wurtzite> piezoelectric_problem;
      piezoelectric_problem.command_line.parse_command_line (argc, argv);
      piezoelectric_problem.run ();
    }

  // ...and if this should fail, try to gather as much information as
  // possible. Specifically, if the exception that was thrown is an
  // object of a class that is derived from the C++ standard class
  // exception.
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }

  // If the exception that was thrown somewhere was not an object of a
  // class derived from the standard exception class,
  // then nothing cane be done at all - simply print an error message
  // and exit.
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  // At this point, the application performed as was expected - return
  // without error.
  return 0;
}
<|endoftext|>


<commit_before>#ifndef COFFEE_MILL_MATRIX
#define COFFEE_MILL_MATRIX
#include <mill/util/is_all.hpp>
#include <array>

namespace mill
{

template<typename realT, std::size_t Row, std::size_t Col>
class Matrix
{
  public:
    using real_type = realT;
    using scalar_type = real_type;
    constexpr static std::size_t dim_row = Row;
    constexpr static std::size_t dim_col = Col;
    constexpr static std::size_t number_of_element = Row * Col;
    using container_type = std::array<real_type, number_of_element>;

  public:
    Matrix() : values_{{}}{}
    ~Matrix() = default;

    template<typename ... T_args, class = typename std::enable_if<
        (sizeof...(T_args) == number_of_element) &&
        is_all<std::is_convertible, realT, T_args...>::value>::type>
    Matrix(T_args ... args) : values_{{args...}}{}

    Matrix(const Matrix& mat) = default;
    Matrix& operator=(const Matrix& mat) = default;

    Matrix& operator+=(const Matrix& mat);
    Matrix& operator-=(const Matrix& mat);
    Matrix& operator*=(const scalar_type& scl);
    Matrix& operator/=(const scalar_type& scl);

    scalar_type  at(const std::size_t i, const std::size_t j) const;
    scalar_type& at(const std::size_t i, const std::size_t j);
    scalar_type  operator()(const std::size_t i, const std::size_t j) const;
    scalar_type& operator()(const std::size_t i, const std::size_t j);

    scalar_type  at(const std::size_t i) const {return values_.at(i);}
    scalar_type& at(const std::size_t i)       {return values_.at(i);}
    scalar_type  operator[](const std::size_t i) const {return values_[i];}
    scalar_type& operator[](const std::size_t i)       {return values_[i];}

  private:
    container_type values_;
};

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator+=(const Matrix<realT, R, C>& mat)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] += mat[i];
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator-=(const Matrix<realT, R, C>& mat)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] -= mat[i];
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator*=(const scalar_type& s)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] *= s;
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator/=(const scalar_type& s)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] /= s;
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator+(const Matrix<realT, R, C>& lhs, const Matrix<realT, R, C>& rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] + rhs[i];
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator-(const Matrix<realT, R, C>& lhs, const Matrix<realT, R, C>& rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] - rhs[i];
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator*(const Matrix<realT, R, C>& lhs, const realT rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] * rhs;
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator*(const realT lhs, const Matrix<realT, R, C>& rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs * rhs[i];
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator/(const Matrix<realT, R, C>& lhs, const realT rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] / rhs;
    return retval;
}

template<typename realT, std::size_t L, std::size_t M, std::size_t N>
Matrix<realT, L, N>
operator*(const Matrix<realT, L, M>& lhs, const Matrix<realT, M, N>& rhs)
{
    Matrix<realT, L, N> retval;
    for(std::size_t i=0; i < L; ++i)
        for(std::size_t j=0; j < N; ++j)
            for(std::size_t k=0; k < M; ++k)
                retval(i, j) += lhs(i, k) * rhs(k, j);
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type
Matrix<realT, R, C>::at(const std::size_t i, const std::size_t j) const
{
    return this->values_.at(i * C + j);
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type&
Matrix<realT, R, C>::at(const std::size_t i, const std::size_t j)
{
    return this->values_.at(i * C + j);
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type
Matrix<realT, R, C>::operator()(const std::size_t i, const std::size_t j) const
{
    return this->values_[i * C + j];
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type&
Matrix<realT, R, C>::operator()(const std::size_t i, const std::size_t j)
{
    return this->values_[i * C + j];
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, C, R> transpose(const Matrix<realT, R, C>& mat)
{
    Matrix<realT, C, R> retval;
    for(std::size_t i=0; i<R; ++i)
        for(std::size_t j=0; j<C; ++j)
            retval(j, i) = mat(i, j);
    return retval;
}

// determinant and inverse is not so efficient.

template<typename realT>
inline realT determinant(const Matrix<realT, 3, 3>& mat)
{
    return mat(0,0) * mat(1,1) * mat(2,2) +
           mat(1,0) * mat(2,1) * mat(0,2) +
           mat(2,0) * mat(0,1) * mat(1,2) -
           mat(0,0) * mat(2,1) * mat(1,2) -
           mat(2,0) * mat(1,1) * mat(0,2) -
           mat(1,0) * mat(0,1) * mat(2,2);
}

template<typename realT>
Matrix<realT, 3, 3> inverse(const Matrix<realT, 3, 3>& mat)
{
    const auto det_inv = 1e0 / determinant(mat);

    Matrix<realT, 3, 3> inv;
    inv(0,0) = det_inv * (mat(1,1) * mat(2,2) - mat(1,2) * mat(2,1));
    inv(1,1) = det_inv * (mat(0,0) * mat(2,2) - mat(0,2) * mat(2,0));
    inv(2,2) = det_inv * (mat(0,0) * mat(1,1) - mat(0,1) * mat(1,0));

    inv(0,1) = det_inv * (mat(0,2) * mat(2,1) - mat(0,1) * mat(2,2));
    inv(0,2) = det_inv * (mat(0,1) * mat(1,2) - mat(0,2) * mat(1,1));
    inv(1,2) = det_inv * (mat(0,2) * mat(1,0) - mat(0,0) * mat(1,2));

    inv(1,0) = det_inv * (mat(1,2) * mat(2,0) - mat(1,0) * mat(2,2));
    inv(2,0) = det_inv * (mat(1,0) * mat(2,1) - mat(2,0) * mat(1,1));
    inv(2,1) = det_inv * (mat(2,0) * mat(0,1) - mat(0,0) * mat(2,1));
    return inv;
}

} // mill
#endif /* COFFEE_MILL_MATRIX */
<commit_msg>add cast in Matrix::ctor<commit_after>#ifndef COFFEE_MILL_MATRIX
#define COFFEE_MILL_MATRIX
#include <mill/util/is_all.hpp>
#include <array>

namespace mill
{

template<typename realT, std::size_t Row, std::size_t Col>
class Matrix
{
  public:
    using real_type = realT;
    using scalar_type = real_type;
    constexpr static std::size_t dim_row = Row;
    constexpr static std::size_t dim_col = Col;
    constexpr static std::size_t number_of_element = Row * Col;
    using container_type = std::array<real_type, number_of_element>;

  public:
    Matrix() : values_{{}}{}
    ~Matrix() = default;

    template<typename ... T_args, class = typename std::enable_if<
        (sizeof...(T_args) == number_of_element) &&
        is_all<std::is_convertible, realT, T_args...>::value>::type>
    Matrix(T_args ... args) : values_{{static_cast<real_type>(args)...}}{}

    Matrix(const Matrix& mat) = default;
    Matrix& operator=(const Matrix& mat) = default;

    Matrix& operator+=(const Matrix& mat);
    Matrix& operator-=(const Matrix& mat);
    Matrix& operator*=(const scalar_type& scl);
    Matrix& operator/=(const scalar_type& scl);

    scalar_type  at(const std::size_t i, const std::size_t j) const;
    scalar_type& at(const std::size_t i, const std::size_t j);
    scalar_type  operator()(const std::size_t i, const std::size_t j) const;
    scalar_type& operator()(const std::size_t i, const std::size_t j);

    scalar_type  at(const std::size_t i) const {return values_.at(i);}
    scalar_type& at(const std::size_t i)       {return values_.at(i);}
    scalar_type  operator[](const std::size_t i) const {return values_[i];}
    scalar_type& operator[](const std::size_t i)       {return values_[i];}

  private:
    container_type values_;
};

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator+=(const Matrix<realT, R, C>& mat)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] += mat[i];
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator-=(const Matrix<realT, R, C>& mat)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] -= mat[i];
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator*=(const scalar_type& s)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] *= s;
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>&
Matrix<realT, R, C>::operator/=(const scalar_type& s)
{
    for(std::size_t i=0; i<number_of_element; ++i)
        this->values_[i] /= s;
    return *this;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator+(const Matrix<realT, R, C>& lhs, const Matrix<realT, R, C>& rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] + rhs[i];
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator-(const Matrix<realT, R, C>& lhs, const Matrix<realT, R, C>& rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] - rhs[i];
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator*(const Matrix<realT, R, C>& lhs, const realT rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] * rhs;
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator*(const realT lhs, const Matrix<realT, R, C>& rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs * rhs[i];
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, R, C>
operator/(const Matrix<realT, R, C>& lhs, const realT rhs)
{
    Matrix<realT, R, C> retval;
    for(std::size_t i=0; i<R*C; ++i)
        retval[i] = lhs[i] / rhs;
    return retval;
}

template<typename realT, std::size_t L, std::size_t M, std::size_t N>
Matrix<realT, L, N>
operator*(const Matrix<realT, L, M>& lhs, const Matrix<realT, M, N>& rhs)
{
    Matrix<realT, L, N> retval;
    for(std::size_t i=0; i < L; ++i)
        for(std::size_t j=0; j < N; ++j)
            for(std::size_t k=0; k < M; ++k)
                retval(i, j) += lhs(i, k) * rhs(k, j);
    return retval;
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type
Matrix<realT, R, C>::at(const std::size_t i, const std::size_t j) const
{
    return this->values_.at(i * C + j);
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type&
Matrix<realT, R, C>::at(const std::size_t i, const std::size_t j)
{
    return this->values_.at(i * C + j);
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type
Matrix<realT, R, C>::operator()(const std::size_t i, const std::size_t j) const
{
    return this->values_[i * C + j];
}

template<typename realT, std::size_t R, std::size_t C>
typename Matrix<realT, R, C>::scalar_type&
Matrix<realT, R, C>::operator()(const std::size_t i, const std::size_t j)
{
    return this->values_[i * C + j];
}

template<typename realT, std::size_t R, std::size_t C>
Matrix<realT, C, R> transpose(const Matrix<realT, R, C>& mat)
{
    Matrix<realT, C, R> retval;
    for(std::size_t i=0; i<R; ++i)
        for(std::size_t j=0; j<C; ++j)
            retval(j, i) = mat(i, j);
    return retval;
}

// determinant and inverse is not so efficient.

template<typename realT>
inline realT determinant(const Matrix<realT, 3, 3>& mat)
{
    return mat(0,0) * mat(1,1) * mat(2,2) +
           mat(1,0) * mat(2,1) * mat(0,2) +
           mat(2,0) * mat(0,1) * mat(1,2) -
           mat(0,0) * mat(2,1) * mat(1,2) -
           mat(2,0) * mat(1,1) * mat(0,2) -
           mat(1,0) * mat(0,1) * mat(2,2);
}

template<typename realT>
Matrix<realT, 3, 3> inverse(const Matrix<realT, 3, 3>& mat)
{
    const auto det_inv = 1e0 / determinant(mat);

    Matrix<realT, 3, 3> inv;
    inv(0,0) = det_inv * (mat(1,1) * mat(2,2) - mat(1,2) * mat(2,1));
    inv(1,1) = det_inv * (mat(0,0) * mat(2,2) - mat(0,2) * mat(2,0));
    inv(2,2) = det_inv * (mat(0,0) * mat(1,1) - mat(0,1) * mat(1,0));

    inv(0,1) = det_inv * (mat(0,2) * mat(2,1) - mat(0,1) * mat(2,2));
    inv(0,2) = det_inv * (mat(0,1) * mat(1,2) - mat(0,2) * mat(1,1));
    inv(1,2) = det_inv * (mat(0,2) * mat(1,0) - mat(0,0) * mat(1,2));

    inv(1,0) = det_inv * (mat(1,2) * mat(2,0) - mat(1,0) * mat(2,2));
    inv(2,0) = det_inv * (mat(1,0) * mat(2,1) - mat(2,0) * mat(1,1));
    inv(2,1) = det_inv * (mat(2,0) * mat(0,1) - mat(0,0) * mat(2,1));
    return inv;
}

} // mill
#endif /* COFFEE_MILL_MATRIX */
<|endoftext|>


<commit_before>// The rule of three

#include <utility>

class foo
{
	public:
		foo()
			: p{new int{5}}
		{ }

		foo(const foo& other)
			: p{new int{*(other.p)}}
		{ }

		foo& operator=(foo other)
		{
			std::swap(p, other.p);
			return *this;
		}

		~foo()
		{
			delete p;
		}

	private:
		int* p;
};

// Implement RAII to encapsulate and safely manage dynamically
// allocated resources.
//
// The class `foo` dynamically allocates an `int` object in its
// constructor ([9]). To ensure that the lifetime of this `int` is
// tied to the lifetime of the `foo` object that allocated it,
// we deallocate it in `foo`'s destructor ([24]). This idiom, known as
// [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization),
// ensures that dynamically allocated objects are properly destroyed
// and no memory is leaked, even if an exception occurs.
//
// We also implement a copy constructor ([12-14]) and an assignment
// operator ([16-20]) to manage this resource when copying or
// assigning a `foo`. In general, if a class has a destructor, it
// almost certainly needs these functions too. This idea is known
// as the *rule of three*.
//
// We can typically avoid manual memory management entirely by using
// existing RAII types.

int main()
{
	foo f;
}
<commit_msg>Link rule of three to rule of zero<commit_after>// The rule of three

#include <utility>

class foo
{
	public:
		foo()
			: p{new int{5}}
		{ }

		foo(const foo& other)
			: p{new int{*(other.p)}}
		{ }

		foo& operator=(foo other)
		{
			std::swap(p, other.p);
			return *this;
		}

		~foo()
		{
			delete p;
		}

	private:
		int* p;
};

// Implement RAII to encapsulate and safely manage dynamically
// allocated resources.
//
// The class `foo` dynamically allocates an `int` object in its
// constructor ([9]). To ensure that the lifetime of this `int` is
// tied to the lifetime of the `foo` object that allocated it,
// we deallocate it in `foo`'s destructor ([24]). This idiom, known as
// [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization),
// ensures that dynamically allocated objects are properly destroyed
// and no memory is leaked, even if an exception occurs.
//
// We also implement a copy constructor ([12-14]) and an assignment
// operator ([16-20]) to manage this resource when copying or
// assigning a `foo`. In general, if a class has a destructor, it
// almost certainly needs these functions too. This idea is known
// as the *rule of three*.
//
// Note that we can typically avoid manual memory management and
// having to write the copy constructor, assignment operator, and
// destructor entirely by using the
// [rule of zero](/common-tasks/rule-of-zero.html)

int main()
{
	foo f;
}
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file ets_airspeed.cpp
 * @author Simon Wilks
 *
 * Driver for the Eagle Tree Airspeed V3 connected via I2C.
 */

#include <nuttx/config.h>

#include <drivers/device/i2c.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>

#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <nuttx/clock.h>

#include <board_config.h>

#include <systemlib/airspeed.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <systemlib/perf_counter.h>

#include <drivers/drv_airspeed.h>
#include <drivers/drv_hrt.h>
#include <drivers/device/ringbuffer.h>

#include <uORB/uORB.h>
#include <uORB/topics/differential_pressure.h>
#include <uORB/topics/subsystem_info.h>
#include <drivers/airspeed/airspeed.h>

/* I2C bus address */
#define I2C_ADDRESS	0x75	/* 7-bit address. 8-bit address is 0xEA */
#define ETS_PATH	"/dev/ets_airspeed"

/* Register address */
#define READ_CMD	0x07	/* Read the data */

/**
 * The Eagle Tree Airspeed V3 cannot provide accurate reading below speeds of 15km/h.
 * You can set this value to 12 if you want a zero reading below 15km/h.
 */
#define MIN_ACCURATE_DIFF_PRES_PA 0

/* Measurement rate is 100Hz */
#define CONVERSION_INTERVAL	(1000000 / 100)	/* microseconds */

class ETSAirspeed : public Airspeed
{
public:
	ETSAirspeed(int bus, int address = I2C_ADDRESS, const char* path = ETS_PATH);

protected:

	/**
	* Perform a poll cycle; collect from the previous measurement
	* and start a new one.
	*/
	virtual void	cycle();
	virtual int	measure();
	virtual int	collect();

};

/*
 * Driver 'main' command.
 */
extern "C" __EXPORT int ets_airspeed_main(int argc, char *argv[]);

ETSAirspeed::ETSAirspeed(int bus, int address, const char* path) : Airspeed(bus, address,
	CONVERSION_INTERVAL, path)
{

}

int
ETSAirspeed::measure()
{
	int ret;

	/*
	 * Send the command to begin a measurement.
	 */
	uint8_t cmd = READ_CMD;
	ret = transfer(&cmd, 1, nullptr, 0);

	if (OK != ret) {
		perf_count(_comms_errors);
	}

	return ret;
}

int
ETSAirspeed::collect()
{
	int	ret = -EIO;

	/* read from the sensor */
	uint8_t val[2] = {0, 0};

	perf_begin(_sample_perf);

	ret = transfer(nullptr, 0, &val[0], 2);

	if (ret < 0) {
		perf_count(_comms_errors);
		return ret;
	}

	uint16_t diff_pres_pa = val[1] << 8 | val[0];
        if (diff_pres_pa == 0) {
		// a zero value means the pressure sensor cannot give us a
		// value. We need to return, and not report a value or the
		// caller could end up using this value as part of an
		// average
		perf_count(_comms_errors);
		log("zero value from sensor");
		return -1;
        }

	if (diff_pres_pa < _diff_pres_offset + MIN_ACCURATE_DIFF_PRES_PA) {
		diff_pres_pa = 0;
	} else {
		diff_pres_pa -= _diff_pres_offset;
	}

	// Track maximum differential pressure measured (so we can work out top speed).
	if (diff_pres_pa > _max_differential_pressure_pa) {
		_max_differential_pressure_pa = diff_pres_pa;
	}

	differential_pressure_s report;
	report.timestamp = hrt_absolute_time();
        report.error_count = perf_event_count(_comms_errors);
	report.differential_pressure_pa = (float)diff_pres_pa;

	// XXX we may want to smooth out the readings to remove noise.
	report.differential_pressure_filtered_pa = (float)diff_pres_pa;
	report.temperature = -1000.0f;
	report.voltage = 0;
	report.max_differential_pressure_pa = _max_differential_pressure_pa;

	if (_airspeed_pub > 0 && !(_pub_blocked)) {
		/* publish it */
		orb_publish(ORB_ID(differential_pressure), _airspeed_pub, &report);
	}

	new_report(report);

	/* notify anyone waiting for data */
	poll_notify(POLLIN);

	ret = OK;

	perf_end(_sample_perf);

	return ret;
}

void
ETSAirspeed::cycle()
{
	int ret;

	/* collection phase? */
	if (_collect_phase) {

		/* perform collection */
		ret = collect();
		if (OK != ret) {
			perf_count(_comms_errors);
			/* restart the measurement state machine */
			start();
			_sensor_ok = false;
			return;
		}

		/* next phase is measurement */
		_collect_phase = false;

		/*
		 * Is there a collect->measure gap?
		 */
		if (_measure_ticks > USEC2TICK(CONVERSION_INTERVAL)) {

			/* schedule a fresh cycle call when we are ready to measure again */
			work_queue(HPWORK,
				   &_work,
				   (worker_t)&Airspeed::cycle_trampoline,
				   this,
				   _measure_ticks - USEC2TICK(CONVERSION_INTERVAL));

			return;
		}
	}

	/* measurement phase */
	ret = measure();
	if (OK != ret) {
		debug("measure error");
	}

	_sensor_ok = (ret == OK);

	/* next phase is collection */
	_collect_phase = true;

	/* schedule a fresh cycle call when the measurement is done */
	work_queue(HPWORK,
		   &_work,
		   (worker_t)&Airspeed::cycle_trampoline,
		   this,
		   USEC2TICK(CONVERSION_INTERVAL));
}

/**
 * Local functions in support of the shell command.
 */
namespace ets_airspeed
{

/* oddly, ERROR is not defined for c++ */
#ifdef ERROR
# undef ERROR
#endif
const int ERROR = -1;

ETSAirspeed	*g_dev;

void	start(int i2c_bus);
void	stop();
void	test();
void	reset();
void	info();

/**
 * Start the driver.
 */
void
start(int i2c_bus)
{
	int fd;

	if (g_dev != nullptr)
		errx(1, "already started");

	/* create the driver */
	g_dev = new ETSAirspeed(i2c_bus);

	if (g_dev == nullptr)
		goto fail;

	if (OK != g_dev->Airspeed::init())
		goto fail;

	/* set the poll rate to default, starts automatic data collection */
	fd = open(AIRSPEED_DEVICE_PATH, O_RDONLY);

	if (fd < 0)
		goto fail;

	if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0)
		goto fail;

	exit(0);

fail:

	if (g_dev != nullptr) {
		delete g_dev;
		g_dev = nullptr;
	}

	errx(1, "no ETS airspeed sensor connected");
}

/**
 * Stop the driver
 */
void
stop()
{
	if (g_dev != nullptr) {
		delete g_dev;
		g_dev = nullptr;

	} else {
		errx(1, "driver not running");
	}

	exit(0);
}

/**
 * Perform some basic functional tests on the driver;
 * make sure we can collect data from the sensor in polled
 * and automatic modes.
 */
void
test()
{
	struct differential_pressure_s report;
	ssize_t sz;
	int ret;

	int fd = open(AIRSPEED_DEVICE_PATH, O_RDONLY);

	if (fd < 0)
		err(1, "%s open failed (try 'ets_airspeed start' if the driver is not running", AIRSPEED_DEVICE_PATH);

	/* do a simple demand read */
	sz = read(fd, &report, sizeof(report));

	if (sz != sizeof(report))
		err(1, "immediate read failed");

	warnx("single read");
	warnx("diff pressure: %d pa", report.differential_pressure_pa);

	/* start the sensor polling at 2Hz */
	if (OK != ioctl(fd, SENSORIOCSPOLLRATE, 2))
		errx(1, "failed to set 2Hz poll rate");

	/* read the sensor 5x and report each value */
	for (unsigned i = 0; i < 5; i++) {
		struct pollfd fds;

		/* wait for data to be ready */
		fds.fd = fd;
		fds.events = POLLIN;
		ret = poll(&fds, 1, 2000);

		if (ret != 1)
			errx(1, "timed out waiting for sensor data");

		/* now go get it */
		sz = read(fd, &report, sizeof(report));

		if (sz != sizeof(report))
			err(1, "periodic read failed");

		warnx("periodic read %u", i);
		warnx("diff pressure: %d pa", report.differential_pressure_pa);
	}

	/* reset the sensor polling to its default rate */
	if (OK != ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT))
		errx(1, "failed to set default rate");

	errx(0, "PASS");
}

/**
 * Reset the driver.
 */
void
reset()
{
	int fd = open(AIRSPEED_DEVICE_PATH, O_RDONLY);

	if (fd < 0)
		err(1, "failed ");

	if (ioctl(fd, SENSORIOCRESET, 0) < 0)
		err(1, "driver reset failed");

	if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0)
		err(1, "driver poll restart failed");

	exit(0);
}

/**
 * Print a little info about the driver.
 */
void
info()
{
	if (g_dev == nullptr)
		errx(1, "driver not running");

	printf("state @ %p\n", g_dev);
	g_dev->print_info();

	exit(0);
}

} // namespace

static void
ets_airspeed_usage()
{
	warnx("usage: ets_airspeed command [options]");
	warnx("options:");
	warnx("\t-b --bus i2cbus (%d)", PX4_I2C_BUS_DEFAULT);
	warnx("command:");
	warnx("\tstart|stop|reset|test|info");
}

int
ets_airspeed_main(int argc, char *argv[])
{
	int i2c_bus = PX4_I2C_BUS_DEFAULT;

	int i;

	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--bus") == 0) {
			if (argc > i + 1) {
				i2c_bus = atoi(argv[i + 1]);
			}
		}
	}

	/*
	 * Start/load the driver.
	 */
	if (!strcmp(argv[1], "start"))
		ets_airspeed::start(i2c_bus);

	/*
	 * Stop the driver
	 */
	if (!strcmp(argv[1], "stop"))
		ets_airspeed::stop();

	/*
	 * Test the driver/device.
	 */
	if (!strcmp(argv[1], "test"))
		ets_airspeed::test();

	/*
	 * Reset the driver.
	 */
	if (!strcmp(argv[1], "reset"))
		ets_airspeed::reset();

	/*
	 * Print driver information.
	 */
	if (!strcmp(argv[1], "info") || !strcmp(argv[1], "status"))
		ets_airspeed::info();

	ets_airspeed_usage();
	exit(0);
}
<commit_msg>ets airspeed: Support raw field<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file ets_airspeed.cpp
 * @author Simon Wilks
 *
 * Driver for the Eagle Tree Airspeed V3 connected via I2C.
 */

#include <nuttx/config.h>

#include <drivers/device/i2c.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>

#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <nuttx/clock.h>

#include <board_config.h>

#include <systemlib/airspeed.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <systemlib/perf_counter.h>

#include <drivers/drv_airspeed.h>
#include <drivers/drv_hrt.h>
#include <drivers/device/ringbuffer.h>

#include <uORB/uORB.h>
#include <uORB/topics/differential_pressure.h>
#include <uORB/topics/subsystem_info.h>
#include <drivers/airspeed/airspeed.h>

/* I2C bus address */
#define I2C_ADDRESS	0x75	/* 7-bit address. 8-bit address is 0xEA */
#define ETS_PATH	"/dev/ets_airspeed"

/* Register address */
#define READ_CMD	0x07	/* Read the data */

/**
 * The Eagle Tree Airspeed V3 cannot provide accurate reading below speeds of 15km/h.
 * You can set this value to 12 if you want a zero reading below 15km/h.
 */
#define MIN_ACCURATE_DIFF_PRES_PA 0

/* Measurement rate is 100Hz */
#define CONVERSION_INTERVAL	(1000000 / 100)	/* microseconds */

class ETSAirspeed : public Airspeed
{
public:
	ETSAirspeed(int bus, int address = I2C_ADDRESS, const char* path = ETS_PATH);

protected:

	/**
	* Perform a poll cycle; collect from the previous measurement
	* and start a new one.
	*/
	virtual void	cycle();
	virtual int	measure();
	virtual int	collect();

};

/*
 * Driver 'main' command.
 */
extern "C" __EXPORT int ets_airspeed_main(int argc, char *argv[]);

ETSAirspeed::ETSAirspeed(int bus, int address, const char* path) : Airspeed(bus, address,
	CONVERSION_INTERVAL, path)
{

}

int
ETSAirspeed::measure()
{
	int ret;

	/*
	 * Send the command to begin a measurement.
	 */
	uint8_t cmd = READ_CMD;
	ret = transfer(&cmd, 1, nullptr, 0);

	if (OK != ret) {
		perf_count(_comms_errors);
	}

	return ret;
}

int
ETSAirspeed::collect()
{
	int	ret = -EIO;

	/* read from the sensor */
	uint8_t val[2] = {0, 0};

	perf_begin(_sample_perf);

	ret = transfer(nullptr, 0, &val[0], 2);

	if (ret < 0) {
		perf_count(_comms_errors);
		return ret;
	}

	uint16_t diff_pres_pa_raw = val[1] << 8 | val[0];
	uint16_t diff_pres_pa;
        if (diff_pres_pa_raw == 0) {
		// a zero value means the pressure sensor cannot give us a
		// value. We need to return, and not report a value or the
		// caller could end up using this value as part of an
		// average
		perf_count(_comms_errors);
		log("zero value from sensor");
		return -1;
        }

	if (diff_pres_pa_raw < _diff_pres_offset + MIN_ACCURATE_DIFF_PRES_PA) {
		diff_pres_pa = 0;
	} else {
		diff_pres_pa = diff_pres_pa_raw - _diff_pres_offset;
	}

	// Track maximum differential pressure measured (so we can work out top speed).
	if (diff_pres_pa > _max_differential_pressure_pa) {
		_max_differential_pressure_pa = diff_pres_pa;
	}

	differential_pressure_s report;
	report.timestamp = hrt_absolute_time();
        report.error_count = perf_event_count(_comms_errors);
	report.differential_pressure_pa = (float)diff_pres_pa;

	// XXX we may want to smooth out the readings to remove noise.
	report.differential_pressure_filtered_pa = (float)diff_pres_pa;
	report.differential_pressure_raw_pa = (float)diff_pres_pa_raw;
	report.temperature = -1000.0f;
	report.voltage = 0;
	report.max_differential_pressure_pa = _max_differential_pressure_pa;

	if (_airspeed_pub > 0 && !(_pub_blocked)) {
		/* publish it */
		orb_publish(ORB_ID(differential_pressure), _airspeed_pub, &report);
	}

	new_report(report);

	/* notify anyone waiting for data */
	poll_notify(POLLIN);

	ret = OK;

	perf_end(_sample_perf);

	return ret;
}

void
ETSAirspeed::cycle()
{
	int ret;

	/* collection phase? */
	if (_collect_phase) {

		/* perform collection */
		ret = collect();
		if (OK != ret) {
			perf_count(_comms_errors);
			/* restart the measurement state machine */
			start();
			_sensor_ok = false;
			return;
		}

		/* next phase is measurement */
		_collect_phase = false;

		/*
		 * Is there a collect->measure gap?
		 */
		if (_measure_ticks > USEC2TICK(CONVERSION_INTERVAL)) {

			/* schedule a fresh cycle call when we are ready to measure again */
			work_queue(HPWORK,
				   &_work,
				   (worker_t)&Airspeed::cycle_trampoline,
				   this,
				   _measure_ticks - USEC2TICK(CONVERSION_INTERVAL));

			return;
		}
	}

	/* measurement phase */
	ret = measure();
	if (OK != ret) {
		debug("measure error");
	}

	_sensor_ok = (ret == OK);

	/* next phase is collection */
	_collect_phase = true;

	/* schedule a fresh cycle call when the measurement is done */
	work_queue(HPWORK,
		   &_work,
		   (worker_t)&Airspeed::cycle_trampoline,
		   this,
		   USEC2TICK(CONVERSION_INTERVAL));
}

/**
 * Local functions in support of the shell command.
 */
namespace ets_airspeed
{

/* oddly, ERROR is not defined for c++ */
#ifdef ERROR
# undef ERROR
#endif
const int ERROR = -1;

ETSAirspeed	*g_dev;

void	start(int i2c_bus);
void	stop();
void	test();
void	reset();
void	info();

/**
 * Start the driver.
 */
void
start(int i2c_bus)
{
	int fd;

	if (g_dev != nullptr)
		errx(1, "already started");

	/* create the driver */
	g_dev = new ETSAirspeed(i2c_bus);

	if (g_dev == nullptr)
		goto fail;

	if (OK != g_dev->Airspeed::init())
		goto fail;

	/* set the poll rate to default, starts automatic data collection */
	fd = open(AIRSPEED_DEVICE_PATH, O_RDONLY);

	if (fd < 0)
		goto fail;

	if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0)
		goto fail;

	exit(0);

fail:

	if (g_dev != nullptr) {
		delete g_dev;
		g_dev = nullptr;
	}

	errx(1, "no ETS airspeed sensor connected");
}

/**
 * Stop the driver
 */
void
stop()
{
	if (g_dev != nullptr) {
		delete g_dev;
		g_dev = nullptr;

	} else {
		errx(1, "driver not running");
	}

	exit(0);
}

/**
 * Perform some basic functional tests on the driver;
 * make sure we can collect data from the sensor in polled
 * and automatic modes.
 */
void
test()
{
	struct differential_pressure_s report;
	ssize_t sz;
	int ret;

	int fd = open(AIRSPEED_DEVICE_PATH, O_RDONLY);

	if (fd < 0)
		err(1, "%s open failed (try 'ets_airspeed start' if the driver is not running", AIRSPEED_DEVICE_PATH);

	/* do a simple demand read */
	sz = read(fd, &report, sizeof(report));

	if (sz != sizeof(report))
		err(1, "immediate read failed");

	warnx("single read");
	warnx("diff pressure: %d pa", report.differential_pressure_pa);

	/* start the sensor polling at 2Hz */
	if (OK != ioctl(fd, SENSORIOCSPOLLRATE, 2))
		errx(1, "failed to set 2Hz poll rate");

	/* read the sensor 5x and report each value */
	for (unsigned i = 0; i < 5; i++) {
		struct pollfd fds;

		/* wait for data to be ready */
		fds.fd = fd;
		fds.events = POLLIN;
		ret = poll(&fds, 1, 2000);

		if (ret != 1)
			errx(1, "timed out waiting for sensor data");

		/* now go get it */
		sz = read(fd, &report, sizeof(report));

		if (sz != sizeof(report))
			err(1, "periodic read failed");

		warnx("periodic read %u", i);
		warnx("diff pressure: %d pa", report.differential_pressure_pa);
	}

	/* reset the sensor polling to its default rate */
	if (OK != ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT))
		errx(1, "failed to set default rate");

	errx(0, "PASS");
}

/**
 * Reset the driver.
 */
void
reset()
{
	int fd = open(AIRSPEED_DEVICE_PATH, O_RDONLY);

	if (fd < 0)
		err(1, "failed ");

	if (ioctl(fd, SENSORIOCRESET, 0) < 0)
		err(1, "driver reset failed");

	if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0)
		err(1, "driver poll restart failed");

	exit(0);
}

/**
 * Print a little info about the driver.
 */
void
info()
{
	if (g_dev == nullptr)
		errx(1, "driver not running");

	printf("state @ %p\n", g_dev);
	g_dev->print_info();

	exit(0);
}

} // namespace

static void
ets_airspeed_usage()
{
	warnx("usage: ets_airspeed command [options]");
	warnx("options:");
	warnx("\t-b --bus i2cbus (%d)", PX4_I2C_BUS_DEFAULT);
	warnx("command:");
	warnx("\tstart|stop|reset|test|info");
}

int
ets_airspeed_main(int argc, char *argv[])
{
	int i2c_bus = PX4_I2C_BUS_DEFAULT;

	int i;

	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--bus") == 0) {
			if (argc > i + 1) {
				i2c_bus = atoi(argv[i + 1]);
			}
		}
	}

	/*
	 * Start/load the driver.
	 */
	if (!strcmp(argv[1], "start"))
		ets_airspeed::start(i2c_bus);

	/*
	 * Stop the driver
	 */
	if (!strcmp(argv[1], "stop"))
		ets_airspeed::stop();

	/*
	 * Test the driver/device.
	 */
	if (!strcmp(argv[1], "test"))
		ets_airspeed::test();

	/*
	 * Reset the driver.
	 */
	if (!strcmp(argv[1], "reset"))
		ets_airspeed::reset();

	/*
	 * Print driver information.
	 */
	if (!strcmp(argv[1], "info") || !strcmp(argv[1], "status"))
		ets_airspeed::info();

	ets_airspeed_usage();
	exit(0);
}
<|endoftext|>