<commit_before>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * 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.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting 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.
****************************************************************************/

#include <boost/test/unit_test.hpp>

#include <pdal/SpatialReference.hpp>
#include <pdal/drivers/las/Reader.hpp>
#include <pdal/filters/ReprojectionFilter.hpp>
#include <pdal/filters/ScalingFilter.hpp>
#include <pdal/Iterator.hpp>
#include <pdal/Schema.hpp>
#include <pdal/SchemaLayout.hpp>
#include <pdal/PointBuffer.hpp>
#include <pdal/exceptions.hpp>

#include "Support.hpp"

BOOST_AUTO_TEST_SUITE(ReprojectionFilterTest)

static void compareBounds(const pdal::Bounds<double>& p, const pdal::Bounds<double>& q)
{
    BOOST_CHECK_CLOSE(p.getMinimum(0), q.getMinimum(0), 1);
    BOOST_CHECK_CLOSE(p.getMinimum(1), q.getMinimum(1), 1);
    BOOST_CHECK_CLOSE(p.getMinimum(2), q.getMinimum(2), 1);
    BOOST_CHECK_CLOSE(p.getMaximum(0), q.getMaximum(0), 1);
    BOOST_CHECK_CLOSE(p.getMaximum(1), q.getMaximum(1), 1);
    BOOST_CHECK_CLOSE(p.getMaximum(2), q.getMaximum(2), 1);
}

static void getPoint(const pdal::PointBuffer& data, double& x, double& y, double& z, double scaleX, double scaleY, double scaleZ)
{
    using namespace pdal;

    const Schema& schema = data.getSchema();

    const int indexX = schema.getDimensionIndex(Dimension::Field_X, Dimension::Int32);
    const int indexY = schema.getDimensionIndex(Dimension::Field_Y, Dimension::Int32);
    const int indexZ = schema.getDimensionIndex(Dimension::Field_Z, Dimension::Int32);

    const boost::int32_t xraw = data.getField<boost::int32_t>(0, indexX);
    const boost::int32_t yraw = data.getField<boost::int32_t>(0, indexY);
    const boost::int32_t zraw = data.getField<boost::int32_t>(0, indexZ);

    x = (double)xraw * scaleX;
    y = (double)yraw * scaleY;
    z = (double)zraw * scaleZ;

    return;
}

// Test reprojecting UTM 15 to DD with a filter
BOOST_AUTO_TEST_CASE(test_1)
{
    const char* epsg4326_wkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]";
    const char* utm15_wkt = "PROJCS[\"NAD83 / UTM zone 15N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.2572221010002,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4269\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-93],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"26915\"]]";

    const double preX = 470692.447538;
    const double preY = 4602888.904642;
    const double preZ = 16.000000;
    const double postX = -93.351563;
    const double postY = 41.577148;
    const double postZ = 16.000000;

    // we compute three answers:
    //   (1) w/out reprojection
    //   (2) with scaling and reproj
    //   (3) with custom scaling and reproj

    //
    // (1)
    //
    {
        pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

        const pdal::SpatialReference in_ref(reader.getSpatialReference());
        const pdal::SpatialReference out_ref(epsg4326_wkt);

        BOOST_CHECK(in_ref.getWKT() == utm15_wkt);
        BOOST_CHECK(out_ref.getWKT() == epsg4326_wkt);

        const pdal::Schema& schema = reader.getSchema();
        const pdal::SchemaLayout layout(schema);
        pdal::PointBuffer data(layout, 1);

        pdal::SequentialIterator* iter = reader.createSequentialIterator();
        boost::uint32_t numRead = iter->read(data);
        BOOST_CHECK(numRead == 1);
        delete iter;

        // note this file has only 1 points, so yes, the extent's mins and maxes are the same
        const pdal::Bounds<double> oldBounds_ref(preX, preY, preZ, preX, preY, preZ);
        const pdal::Bounds<double>& oldBounds = reader.getBounds();
        compareBounds(oldBounds_ref, oldBounds);

        double x=0, y=0, z=0;
        getPoint(data, x, y, z, 0.01, 0.01, 0.01);

        BOOST_CHECK_CLOSE(x, preX, 1);
        BOOST_CHECK_CLOSE(y, preY, 1);
        BOOST_CHECK_CLOSE(z, preZ, 1);
    }

    //
    // (2)
    //
    {
        pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

        const pdal::SpatialReference in_ref(reader.getSpatialReference());
        const pdal::SpatialReference out_ref(epsg4326_wkt);

        pdal::filters::ScalingFilter scalingFilter(reader, false);
        pdal::filters::ReprojectionFilter reprojectionFilter(scalingFilter, in_ref, out_ref);
        pdal::filters::ScalingFilter descalingFilter(reprojectionFilter, true);

        const pdal::Schema& schema = descalingFilter.getSchema();
        const pdal::SchemaLayout layout(schema);
        pdal::PointBuffer data(layout, 1);

        pdal::SequentialIterator* iter = descalingFilter.createSequentialIterator();
        boost::uint32_t numRead = iter->read(data);
        BOOST_CHECK(numRead == 1);
        delete iter;

        const pdal::Bounds<double> newBounds_ref(postX, postY, postZ, postX, postY, postZ);
        const pdal::Bounds<double>& newBounds = descalingFilter.getBounds();
        compareBounds(newBounds_ref, newBounds);

        double x=0, y=0, z=0;
        getPoint(data, x, y, z, 0.01, 0.01, 0.01);

        BOOST_CHECK_CLOSE(x, postX, 1);
        BOOST_CHECK_CLOSE(y, postY, 1);
        BOOST_CHECK_CLOSE(z, postZ, 1);
    }

    //
    // (3)
    //
    {
        pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

        const pdal::SpatialReference in_ref(reader.getSpatialReference());
        const pdal::SpatialReference out_ref(epsg4326_wkt);

        // convert to doubles, use internal scale factor
        pdal::filters::ScalingFilter scalingFilter(reader, false);

        pdal::filters::ReprojectionFilter reprojectionFilter(scalingFilter, in_ref, out_ref);

        // convert to ints, using custom scale factor
        pdal::filters::ScalingFilter descalingFilter(reprojectionFilter, 0.000001, 0.0, 0.000001, 0.0, 0.01, 0.0, true);

        const pdal::Schema& schema = descalingFilter.getSchema();
        const pdal::SchemaLayout layout(schema);
        pdal::PointBuffer data2(layout, 1);

        pdal::SequentialIterator* iter = descalingFilter.createSequentialIterator();
        boost::uint32_t numRead = iter->read(data2);
        BOOST_CHECK(numRead == 1);
        delete iter;

        double x=0, y=0, z=0;
        getPoint(data2, x, y, z, 0.000001, 0.000001, 0.01);

        BOOST_CHECK_CLOSE(x, postX, 1);
        BOOST_CHECK_CLOSE(y, postY, 1);
        BOOST_CHECK_CLOSE(z, postZ, 1);
    }

    return;
}

BOOST_AUTO_TEST_CASE(test_impedence_mismatch)
{
    pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

    const pdal::SpatialReference& in_ref = reader.getSpatialReference();

    const char* epsg4326_wkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]";

    pdal::SpatialReference out_ref;
    out_ref.setWKT(epsg4326_wkt);

    bool ok = false;
    try
    {
        pdal::filters::ReprojectionFilter filter(reader, in_ref, out_ref);
        ok = false;
    }
    catch (pdal::impedance_invalid&)
    {
        ok = true;
    }
    BOOST_CHECK(ok == true);

    return;
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>start using operator== for pdal::SpatialReference comparisons<commit_after>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * 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.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting 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.
****************************************************************************/

#include <boost/test/unit_test.hpp>

#include <pdal/SpatialReference.hpp>
#include <pdal/drivers/las/Reader.hpp>
#include <pdal/filters/ReprojectionFilter.hpp>
#include <pdal/filters/ScalingFilter.hpp>
#include <pdal/Iterator.hpp>
#include <pdal/Schema.hpp>
#include <pdal/SchemaLayout.hpp>
#include <pdal/PointBuffer.hpp>
#include <pdal/exceptions.hpp>

#include "Support.hpp"

BOOST_AUTO_TEST_SUITE(ReprojectionFilterTest)

static void compareBounds(const pdal::Bounds<double>& p, const pdal::Bounds<double>& q)
{
    BOOST_CHECK_CLOSE(p.getMinimum(0), q.getMinimum(0), 1);
    BOOST_CHECK_CLOSE(p.getMinimum(1), q.getMinimum(1), 1);
    BOOST_CHECK_CLOSE(p.getMinimum(2), q.getMinimum(2), 1);
    BOOST_CHECK_CLOSE(p.getMaximum(0), q.getMaximum(0), 1);
    BOOST_CHECK_CLOSE(p.getMaximum(1), q.getMaximum(1), 1);
    BOOST_CHECK_CLOSE(p.getMaximum(2), q.getMaximum(2), 1);
}

static void getPoint(const pdal::PointBuffer& data, double& x, double& y, double& z, double scaleX, double scaleY, double scaleZ)
{
    using namespace pdal;

    const Schema& schema = data.getSchema();

    const int indexX = schema.getDimensionIndex(Dimension::Field_X, Dimension::Int32);
    const int indexY = schema.getDimensionIndex(Dimension::Field_Y, Dimension::Int32);
    const int indexZ = schema.getDimensionIndex(Dimension::Field_Z, Dimension::Int32);

    const boost::int32_t xraw = data.getField<boost::int32_t>(0, indexX);
    const boost::int32_t yraw = data.getField<boost::int32_t>(0, indexY);
    const boost::int32_t zraw = data.getField<boost::int32_t>(0, indexZ);

    x = (double)xraw * scaleX;
    y = (double)yraw * scaleY;
    z = (double)zraw * scaleZ;

    return;
}

// Test reprojecting UTM 15 to DD with a filter
BOOST_AUTO_TEST_CASE(test_1)
{
    const char* epsg4326_wkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]";
    const char* utm15_wkt = "PROJCS[\"NAD83 / UTM zone 15N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.2572221010002,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4269\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-93],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"26915\"]]";

    const double preX = 470692.447538;
    const double preY = 4602888.904642;
    const double preZ = 16.000000;
    const double postX = -93.351563;
    const double postY = 41.577148;
    const double postZ = 16.000000;

    // we compute three answers:
    //   (1) w/out reprojection
    //   (2) with scaling and reproj
    //   (3) with custom scaling and reproj

    //
    // (1)
    //
    {
        pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

        const pdal::SpatialReference in_ref_test(utm15_wkt);
        const pdal::SpatialReference out_ref_test(epsg4326_wkt);

        const pdal::SpatialReference in_ref(reader.getSpatialReference());
        const pdal::SpatialReference out_ref(epsg4326_wkt);

        BOOST_CHECK_EQUAL(in_ref, in_ref_test);
        BOOST_CHECK(out_ref == out_ref_test);

        const pdal::Schema& schema = reader.getSchema();
        const pdal::SchemaLayout layout(schema);
        pdal::PointBuffer data(layout, 1);

        pdal::SequentialIterator* iter = reader.createSequentialIterator();
        boost::uint32_t numRead = iter->read(data);
        BOOST_CHECK(numRead == 1);
        delete iter;

        // note this file has only 1 points, so yes, the extent's mins and maxes are the same
        const pdal::Bounds<double> oldBounds_ref(preX, preY, preZ, preX, preY, preZ);
        const pdal::Bounds<double>& oldBounds = reader.getBounds();
        compareBounds(oldBounds_ref, oldBounds);

        double x=0, y=0, z=0;
        getPoint(data, x, y, z, 0.01, 0.01, 0.01);

        BOOST_CHECK_CLOSE(x, preX, 1);
        BOOST_CHECK_CLOSE(y, preY, 1);
        BOOST_CHECK_CLOSE(z, preZ, 1);
    }

    //
    // (2)
    //
    {
        pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

        const pdal::SpatialReference in_ref(reader.getSpatialReference());
        const pdal::SpatialReference out_ref(epsg4326_wkt);

        pdal::filters::ScalingFilter scalingFilter(reader, false);
        pdal::filters::ReprojectionFilter reprojectionFilter(scalingFilter, in_ref, out_ref);
        pdal::filters::ScalingFilter descalingFilter(reprojectionFilter, true);

        const pdal::Schema& schema = descalingFilter.getSchema();
        const pdal::SchemaLayout layout(schema);
        pdal::PointBuffer data(layout, 1);

        pdal::SequentialIterator* iter = descalingFilter.createSequentialIterator();
        boost::uint32_t numRead = iter->read(data);
        BOOST_CHECK(numRead == 1);
        delete iter;

        const pdal::Bounds<double> newBounds_ref(postX, postY, postZ, postX, postY, postZ);
        const pdal::Bounds<double>& newBounds = descalingFilter.getBounds();
        compareBounds(newBounds_ref, newBounds);

        double x=0, y=0, z=0;
        getPoint(data, x, y, z, 0.01, 0.01, 0.01);

        BOOST_CHECK_CLOSE(x, postX, 1);
        BOOST_CHECK_CLOSE(y, postY, 1);
        BOOST_CHECK_CLOSE(z, postZ, 1);
    }

    //
    // (3)
    //
    {
        pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

        const pdal::SpatialReference in_ref(reader.getSpatialReference());
        const pdal::SpatialReference out_ref(epsg4326_wkt);

        // convert to doubles, use internal scale factor
        pdal::filters::ScalingFilter scalingFilter(reader, false);

        pdal::filters::ReprojectionFilter reprojectionFilter(scalingFilter, in_ref, out_ref);

        // convert to ints, using custom scale factor
        pdal::filters::ScalingFilter descalingFilter(reprojectionFilter, 0.000001, 0.0, 0.000001, 0.0, 0.01, 0.0, true);

        const pdal::Schema& schema = descalingFilter.getSchema();
        const pdal::SchemaLayout layout(schema);
        pdal::PointBuffer data2(layout, 1);

        pdal::SequentialIterator* iter = descalingFilter.createSequentialIterator();
        boost::uint32_t numRead = iter->read(data2);
        BOOST_CHECK(numRead == 1);
        delete iter;

        double x=0, y=0, z=0;
        getPoint(data2, x, y, z, 0.000001, 0.000001, 0.01);

        BOOST_CHECK_CLOSE(x, postX, 1);
        BOOST_CHECK_CLOSE(y, postY, 1);
        BOOST_CHECK_CLOSE(z, postZ, 1);
    }

    return;
}

BOOST_AUTO_TEST_CASE(test_impedence_mismatch)
{
    pdal::drivers::las::LasReader reader(Support::datapath("utm15.las"));

    const pdal::SpatialReference& in_ref = reader.getSpatialReference();

    const char* epsg4326_wkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]";

    pdal::SpatialReference out_ref;
    out_ref.setWKT(epsg4326_wkt);

    bool ok = false;
    try
    {
        pdal::filters::ReprojectionFilter filter(reader, in_ref, out_ref);
        ok = false;
    }
    catch (pdal::impedance_invalid&)
    {
        ok = true;
    }
    BOOST_CHECK(ok == true);

    return;
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include "lcd.h"
#include <TFT.h>  // Arduino LCD library

// pin definition for LCD
#define LCD_CS   10
#define LCD_DC   9
#define LCD_RST  8

namespace LCD {
  TFT TFTscreen = TFT(LCD_CS, LCD_DC, LCD_RST);

  void setup() {
    // initialize the display
    TFTscreen.begin();
    // clear the screen with a black background
    TFTscreen.background(0, 0, 0);
    // set the font color to white
    TFTscreen.stroke(255, 255, 255);
  }

  void clearScreen() {
    // clear the screen to show refreshed data
    TFTscreen.background(0, 0, 0);
  }

  void displayTemp(int tempInt) {
    char tempChar[6];
    snprintf(tempChar, sizeof tempChar, "%d", tempInt);
    // print temp text
    TFTscreen.text(tempChar, 0, 20);
  }
}
<commit_msg>Update libraries to reflect screen hardware change<commit_after>#include "display.h"
#include "sensor.h"
#include <LiquidCrystal.h>  // Arduino LCD library
#include <Arduino.h>  // enables use of byte pics

// pin definition for LCD
#define LCD_CS   10
#define LCD_DC   9
#define LCD_RST  8

namespace LCD {
  TFT TFTscreen = TFT(LCD_CS, LCD_DC, LCD_RST);

  void setup() {
    // initialize the display
    TFTscreen.begin();
    // clear the screen with a black background
    TFTscreen.background(0, 0, 0);
    // set the font color to white
    TFTscreen.stroke(255, 255, 255);
  }

  void clearScreen() {
    // clear the screen to show refreshed data
    TFTscreen.background(0, 0, 0);
  }

  void displayTemp(int tempInt) {
    char tempChar[6];
    snprintf(tempChar, sizeof tempChar, "%d", tempInt);
    // print temp text
    TFTscreen.text(tempChar, 0, 20);
  }
}
<|endoftext|>


<commit_before>#include "display.h"
#include "lr35902.h"
#include "memory.h"

void Display::update(uint cycles)
{
  scanline_counter -= cycles;

  if (scanline_counter <= 0)
  {
    scanline_counter += cycles_per_scanline;

    u8 scanline = memory.get8(Memory::IO::LY) + 1;
    if (scanline > 153)
    {
      scanline = 0;
    }
    memory.direct_io_write8(Memory::IO::LY, scanline);

    if (scanline < 144)
    {
      draw_scanline();
    }
    else if (scanline == 144)
    {
      cpu.raise_interrupt(LR35902::Interrupt::VBLANK);
    }
  }

  update_status(); // put at beginning?
}

void Display::draw_scanline()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);

  if (LCDC & (1<<0))
  {
    draw_background();
  }
  if (LCDC & (1<<5))
  {
    draw_window();
  }
  if (LCDC & (1<<1))
  {
    draw_sprites();
  }
}

void Display::draw_background()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);

  uint base_tile_map_addr;
  if (LCDC & (1<<3))
  {
    // 0x9C00 - 9FFF
    base_tile_map_addr = 0x9C00;
  }
  else
  {
    // 0x9800 - 9BFF
    base_tile_map_addr = 0x9800;
  }

  uint base_tile_data_addr;
  bool signed_pattern_numbers;
  if (LCDC & (1<<4))
  {
    // 0x8000 - 0x8FFF
    // Offsets will be unsigned
    base_tile_data_addr = 0x8000;
    signed_pattern_numbers = false;
  }
  else
  {
    // 0x8800 - 0x97FF
    // Offsets will be signed, set addr to middle of range
    base_tile_data_addr = 0x9000;
    signed_pattern_numbers = true;
  }

  u8 SCY = memory.get8(Memory::IO::SCY);
  u8 SCX = memory.get8(Memory::IO::SCX);

  uint background_y = (SCY + LY)%256;
  uint tile_row = background_y/8;
  uint tile_y = background_y%8;

  for (uint screenx=0; screenx<width; screenx++)
  {
    uint background_x = (SCX + screenx)%256;
    uint tile_col = background_x/8;
    uint tile_x = background_x%8;

    // Tile map is 32x32 tiles, with 1 byte per tile
    uint tile_map_addr = base_tile_map_addr + tile_row*32 + tile_col;
    u8 tile_num = memory.get8(tile_map_addr);

    // Tile data is 32x32 tiles, with 16 bytes per tile
    uint tile_data_addr;
    if (signed_pattern_numbers)
    {
      tile_data_addr = base_tile_data_addr + ((s8)tile_num)*16;
    }
    else
    {
      tile_data_addr = base_tile_data_addr + ((u8)tile_num)*16;
    }

    // Tiles are 8x8 pixels, with 2 bits per pixel
    // i.e 2 bytes per line and 4 pixels per byte
    //   The 2 bits per pixel aren't adjacent, they are in the
    //   same position in each of the 2 bytes for their line.
    uint tile_byte_offset = tile_y*2;
    u8 tile_byte1 = memory.get16(tile_data_addr + tile_byte_offset);
    u8 tile_byte2 = memory.get16(tile_data_addr + tile_byte_offset + 1);

    uint bit = 7 - tile_x;
    uint colour_id = ((tile_byte1 >> bit) & 0x1) |
                     ((tile_byte2 >> bit) & 0x1) << 1;

    // Get the colour from the background palette register
    // 0 = white, 3 = black
    u8 BGP = memory.get8(Memory::IO::BGP);
    u8 colour = (BGP >> (colour_id * 2)) & 0x3;

    framebuffer[LY][screenx] = display_palette[colour];
  }
}

void Display::draw_window()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);
  u8 WY   = memory.get8(Memory::IO::WY);
  u8 WX   = memory.get8(Memory::IO::WX);

  if (WY > LY || WX >= 167)
  {
    // Window isn't visible / not drawn on this scanline
    return;
  }

  uint base_window_map_addr;
  if (LCDC & (1<<6))
  {
    // 0x9C00 - 9FFF
    base_window_map_addr = 0x9C00;
  }
  else
  {
    // 0x9800 - 9BFF
    base_window_map_addr = 0x9800;
  }

  uint base_tile_data_addr;
  bool signed_pattern_numbers;
  if (LCDC & (1<<4))
  {
    // 0x8000 - 0x8FFF
    // Offsets will be unsigned
    base_tile_data_addr = 0x8000;
    signed_pattern_numbers = false;
  }
  else
  {
    // 0x8800 - 0x97FF
    // Offsets will be signed, set addr to middle of range
    base_tile_data_addr = 0x9000;
    signed_pattern_numbers = true;
  }

  uint tile_row = LY/8;
  uint tile_y = LY%8;

  for (uint screenx=0; screenx<width; screenx++)
  {
    uint tile_col = screenx/8;
    uint tile_x = screenx%8;

    // Tile map is 32x32 tiles, with 1 byte per tile
    uint tile_map_addr = base_window_map_addr + tile_row*32 + tile_col;
    u8 tile_num = memory.get8(tile_map_addr);

    // Tile data is 32x32 tiles, with 16 bytes per tile
    uint tile_data_addr;
    if (signed_pattern_numbers)
    {
      tile_data_addr = base_tile_data_addr + ((s8)tile_num)*16;
    }
    else
    {
      tile_data_addr = base_tile_data_addr + ((u8)tile_num)*16;
    }

    // Tiles are 8x8 pixels, with 2 bits per pixel
    // i.e 2 bytes per line and 4 pixels per byte
    //   The 2 bits per pixel aren't adjacent, they are in the
    //   same position in each of the 2 bytes for their line.
    uint tile_byte_offset = tile_y*2;
    u8 tile_byte1 = memory.get16(tile_data_addr + tile_byte_offset);
    u8 tile_byte2 = memory.get16(tile_data_addr + tile_byte_offset + 1);

    uint bit = 7 - tile_x;
    uint colour_id = ((tile_byte1 >> bit) & 0x1) |
                     ((tile_byte2 >> bit) & 0x1) << 1;

    // Get the colour from the background palette register
    // 0 = white, 3 = black
    u8 BGP = memory.get8(Memory::IO::BGP);
    u8 colour = (BGP >> (colour_id * 2)) & 0x3;

    framebuffer[LY][screenx] = display_palette[colour];
  }
}

void Display::draw_sprites()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);

  const uint base_sprite_data_addr = 0x8000;
  const uint base_sprite_attr_addr = 0xfe00;

  bool use_8x16_sprites = (LCDC >> 2) & 0x1;
  uint sprite_height = use_8x16_sprites ? 16 : 8;

  // Go through all the sprites in reverse order
  // so sprite 0 is drawn on top of sprite 39 etc.
  for (int i=39; i>=0; i--)
  {
    // Sprite attribute blocks are 4 bytes each
    uint sprite_attr_addr = base_sprite_attr_addr + i*4;

    u8 y_pos = memory.get8(sprite_attr_addr);
    u8 x_pos = memory.get8(sprite_attr_addr + 1);
    u8 pattern_number = memory.get8(sprite_attr_addr + 2);
    u8 flags = memory.get8(sprite_attr_addr + 3);

    // Set x and y coordinates to sprite's top left corner
    y_pos -= 16;
    x_pos -= 8;

    if (y_pos > LY || y_pos + sprite_height <= LY)
    {
      // This sprite doesn't appear on the current scanline
      continue;
    }

    if (use_8x16_sprites)
    {
      pattern_number &= 0xfe; // Set LSB to 0
    }

    bool low_priority = (flags >> 7) & 0x1;
    bool flip_y = (flags >> 6) & 0x1;
    bool flip_x = (flags >> 5) & 0x1;
    uint palette_num = (flags >> 4) & 0x1;

    // Sprites are either 8x8 or 8x16 pixels, with eiter 16 or 32 bytes each
    uint sprite_size = 2 * sprite_height;
    uint sprite_data_addr = base_sprite_data_addr + pattern_number*sprite_size;

    uint sprite_y = LY - y_pos;
    if (flip_y)
    {
      sprite_y = sprite_height - sprite_y;
    }

    // Get the data for this line of the sprite
    uint sprite_byte_offset = sprite_y*2;
    u8 sprite_byte1 = memory.get16(sprite_data_addr + sprite_byte_offset);
    u8 sprite_byte2 = memory.get16(sprite_data_addr + sprite_byte_offset + 1);

    for (uint sprite_x=0; sprite_x<8; sprite_x++)
    {
      uint bit = flip_x ? sprite_x : 7 - sprite_x;
      uint colour_id = ((sprite_byte1 >> bit) & 0x1) |
                       ((sprite_byte2 >> bit) & 0x1) << 1;

      // Get the colour from the background palette register
      // 0 = white, 3 = black
      u8 palette;
      if (palette_num == 0)
      {
        palette = memory.get8(Memory::IO::OBP0);
      }
      else
      {
        palette = memory.get8(Memory::IO::OBP1);
      }
      u8 colour = (palette >> (colour_id * 2)) & 0x3;

      if (x_pos != 0)
      {
        x_pos++;
        x_pos--;
      }
      if (colour != 0) // White is transparent
      {
        // Low priority sprites are only drawn on white backgrounds
        if (!low_priority ||
            (framebuffer[LY][x_pos+sprite_x].r == display_palette[0].r &&
             framebuffer[LY][x_pos+sprite_x].b == display_palette[0].b &&
             framebuffer[LY][x_pos+sprite_x].g == display_palette[0].g))
        {
          framebuffer[LY][x_pos+sprite_x] = display_palette[colour];
        }
      }
    }
  }
}

void Display::update_status()
{
  u8 STAT = memory.get8(Memory::IO::STAT);

  // TODO don't always reset this
  STAT = 252;

  u8 scanline = memory.get8(Memory::IO::LY);
  MODE::Mode mode;

  if (scanline > 144)
  {
    mode = MODE::VBLANK;
  }
  else if (scanline_counter > cycles_per_scanline - 80)
  {
    mode = MODE::OAM;
  }
  else if (scanline_counter > cycles_per_scanline - 80 - 172)
  {
    mode = MODE::VRAM;
  }
  else
  {
    mode = MODE::HBLANK;
  }

  const MODE::Mode current_mode = static_cast<MODE::Mode>(STAT & 0x3);
  if (current_mode != mode &&
      mode != MODE::VRAM &&
      (STAT >> (mode + 3)) & 0x1)
  {
    cpu.raise_interrupt(LR35902::Interrupt::LCD);
  }

  // Update mode
  STAT &= ~0x3;
  STAT |= mode;

  const u8 LYC = memory.get8(Memory::IO::LYC);
  if (LYC == scanline)
  {
    // Set coincidence flag
    STAT |= (1<<2);
    if ((STAT >> 6) & 0x1)
    {
      cpu.raise_interrupt(LR35902::Interrupt::LCD);
    }
  }
  else
  {
    // Clear coincidence flag
    STAT &= ~(1<<2);
  }

  memory.set8(Memory::IO::STAT, STAT);
}
<commit_msg>Display: Use correct X and Y window offsets<commit_after>#include "display.h"
#include "lr35902.h"
#include "memory.h"

void Display::update(uint cycles)
{
  scanline_counter -= cycles;

  if (scanline_counter <= 0)
  {
    scanline_counter += cycles_per_scanline;

    u8 scanline = memory.get8(Memory::IO::LY) + 1;
    if (scanline > 153)
    {
      scanline = 0;
    }
    memory.direct_io_write8(Memory::IO::LY, scanline);

    if (scanline < 144)
    {
      draw_scanline();
    }
    else if (scanline == 144)
    {
      cpu.raise_interrupt(LR35902::Interrupt::VBLANK);
    }
  }

  update_status(); // put at beginning?
}

void Display::draw_scanline()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);

  if (LCDC & (1<<0))
  {
    draw_background();
  }
  if (LCDC & (1<<5))
  {
    draw_window();
  }
  if (LCDC & (1<<1))
  {
    draw_sprites();
  }
}

void Display::draw_background()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);

  uint base_tile_map_addr;
  if (LCDC & (1<<3))
  {
    // 0x9C00 - 9FFF
    base_tile_map_addr = 0x9C00;
  }
  else
  {
    // 0x9800 - 9BFF
    base_tile_map_addr = 0x9800;
  }

  uint base_tile_data_addr;
  bool signed_pattern_numbers;
  if (LCDC & (1<<4))
  {
    // 0x8000 - 0x8FFF
    // Offsets will be unsigned
    base_tile_data_addr = 0x8000;
    signed_pattern_numbers = false;
  }
  else
  {
    // 0x8800 - 0x97FF
    // Offsets will be signed, set addr to middle of range
    base_tile_data_addr = 0x9000;
    signed_pattern_numbers = true;
  }

  u8 SCY = memory.get8(Memory::IO::SCY);
  u8 SCX = memory.get8(Memory::IO::SCX);

  uint background_y = (SCY + LY)%256;
  uint tile_row = background_y/8;
  uint tile_y = background_y%8;

  for (uint screenx=0; screenx<width; screenx++)
  {
    uint background_x = (SCX + screenx)%256;
    uint tile_col = background_x/8;
    uint tile_x = background_x%8;

    // Tile map is 32x32 tiles, with 1 byte per tile
    uint tile_map_addr = base_tile_map_addr + tile_row*32 + tile_col;
    u8 tile_num = memory.get8(tile_map_addr);

    // Tile data is 32x32 tiles, with 16 bytes per tile
    uint tile_data_addr;
    if (signed_pattern_numbers)
    {
      tile_data_addr = base_tile_data_addr + ((s8)tile_num)*16;
    }
    else
    {
      tile_data_addr = base_tile_data_addr + ((u8)tile_num)*16;
    }

    // Tiles are 8x8 pixels, with 2 bits per pixel
    // i.e 2 bytes per line and 4 pixels per byte
    //   The 2 bits per pixel aren't adjacent, they are in the
    //   same position in each of the 2 bytes for their line.
    uint tile_byte_offset = tile_y*2;
    u8 tile_byte1 = memory.get16(tile_data_addr + tile_byte_offset);
    u8 tile_byte2 = memory.get16(tile_data_addr + tile_byte_offset + 1);

    uint bit = 7 - tile_x;
    uint colour_id = ((tile_byte1 >> bit) & 0x1) |
                     ((tile_byte2 >> bit) & 0x1) << 1;

    // Get the colour from the background palette register
    // 0 = white, 3 = black
    u8 BGP = memory.get8(Memory::IO::BGP);
    u8 colour = (BGP >> (colour_id * 2)) & 0x3;

    framebuffer[LY][screenx] = display_palette[colour];
  }
}

void Display::draw_window()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);
  u8 WY   = memory.get8(Memory::IO::WY);
  u8 WX   = memory.get8(Memory::IO::WX);

  if (WY > LY || WX >= 167)
  {
    // Window isn't visible / not drawn on this scanline
    return;
  }

  uint base_window_map_addr;
  if (LCDC & (1<<6))
  {
    // 0x9C00 - 9FFF
    base_window_map_addr = 0x9C00;
  }
  else
  {
    // 0x9800 - 9BFF
    base_window_map_addr = 0x9800;
  }

  uint base_tile_data_addr;
  bool signed_pattern_numbers;
  if (LCDC & (1<<4))
  {
    // 0x8000 - 0x8FFF
    // Offsets will be unsigned
    base_tile_data_addr = 0x8000;
    signed_pattern_numbers = false;
  }
  else
  {
    // 0x8800 - 0x97FF
    // Offsets will be signed, set addr to middle of range
    base_tile_data_addr = 0x9000;
    signed_pattern_numbers = true;
  }

  uint window_y = (LY - WY)%256;
  uint tile_row = window_y/8;
  uint tile_y = window_y%8;

  // Set WX to left side of window
  WX -= 7;

  for (uint screenx=0; screenx<width; screenx++)
  {
    uint window_x = (screenx - WX)%256;
    uint tile_col = window_x/8;
    uint tile_x = window_x%8;

    // Tile map is 32x32 tiles, with 1 byte per tile
    uint tile_map_addr = base_window_map_addr + tile_row*32 + tile_col;
    u8 tile_num = memory.get8(tile_map_addr);

    // Tile data is 32x32 tiles, with 16 bytes per tile
    uint tile_data_addr;
    if (signed_pattern_numbers)
    {
      tile_data_addr = base_tile_data_addr + ((s8)tile_num)*16;
    }
    else
    {
      tile_data_addr = base_tile_data_addr + ((u8)tile_num)*16;
    }

    // Tiles are 8x8 pixels, with 2 bits per pixel
    // i.e 2 bytes per line and 4 pixels per byte
    //   The 2 bits per pixel aren't adjacent, they are in the
    //   same position in each of the 2 bytes for their line.
    uint tile_byte_offset = tile_y*2;
    u8 tile_byte1 = memory.get16(tile_data_addr + tile_byte_offset);
    u8 tile_byte2 = memory.get16(tile_data_addr + tile_byte_offset + 1);

    uint bit = 7 - tile_x;
    uint colour_id = ((tile_byte1 >> bit) & 0x1) |
                     ((tile_byte2 >> bit) & 0x1) << 1;

    // Get the colour from the background palette register
    // 0 = white, 3 = black
    u8 BGP = memory.get8(Memory::IO::BGP);
    u8 colour = (BGP >> (colour_id * 2)) & 0x3;

    framebuffer[LY][screenx] = display_palette[colour];
  }
}

void Display::draw_sprites()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);

  const uint base_sprite_data_addr = 0x8000;
  const uint base_sprite_attr_addr = 0xfe00;

  bool use_8x16_sprites = (LCDC >> 2) & 0x1;
  uint sprite_height = use_8x16_sprites ? 16 : 8;

  // Go through all the sprites in reverse order
  // so sprite 0 is drawn on top of sprite 39 etc.
  for (int i=39; i>=0; i--)
  {
    // Sprite attribute blocks are 4 bytes each
    uint sprite_attr_addr = base_sprite_attr_addr + i*4;

    u8 y_pos = memory.get8(sprite_attr_addr);
    u8 x_pos = memory.get8(sprite_attr_addr + 1);
    u8 pattern_number = memory.get8(sprite_attr_addr + 2);
    u8 flags = memory.get8(sprite_attr_addr + 3);

    // Set x and y coordinates to sprite's top left corner
    y_pos -= 16;
    x_pos -= 8;

    if (y_pos > LY || y_pos + sprite_height <= LY)
    {
      // This sprite doesn't appear on the current scanline
      continue;
    }

    if (use_8x16_sprites)
    {
      pattern_number &= 0xfe; // Set LSB to 0
    }

    bool low_priority = (flags >> 7) & 0x1;
    bool flip_y = (flags >> 6) & 0x1;
    bool flip_x = (flags >> 5) & 0x1;
    uint palette_num = (flags >> 4) & 0x1;

    // Sprites are either 8x8 or 8x16 pixels, with eiter 16 or 32 bytes each
    uint sprite_size = 2 * sprite_height;
    uint sprite_data_addr = base_sprite_data_addr + pattern_number*sprite_size;

    uint sprite_y = LY - y_pos;
    if (flip_y)
    {
      sprite_y = sprite_height - sprite_y;
    }

    // Get the data for this line of the sprite
    uint sprite_byte_offset = sprite_y*2;
    u8 sprite_byte1 = memory.get16(sprite_data_addr + sprite_byte_offset);
    u8 sprite_byte2 = memory.get16(sprite_data_addr + sprite_byte_offset + 1);

    for (uint sprite_x=0; sprite_x<8; sprite_x++)
    {
      uint bit = flip_x ? sprite_x : 7 - sprite_x;
      uint colour_id = ((sprite_byte1 >> bit) & 0x1) |
                       ((sprite_byte2 >> bit) & 0x1) << 1;

      // Get the colour from the background palette register
      // 0 = white, 3 = black
      u8 palette;
      if (palette_num == 0)
      {
        palette = memory.get8(Memory::IO::OBP0);
      }
      else
      {
        palette = memory.get8(Memory::IO::OBP1);
      }
      u8 colour = (palette >> (colour_id * 2)) & 0x3;

      if (x_pos != 0)
      {
        x_pos++;
        x_pos--;
      }
      if (colour != 0) // White is transparent
      {
        // Low priority sprites are only drawn on white backgrounds
        if (!low_priority ||
            (framebuffer[LY][x_pos+sprite_x].r == display_palette[0].r &&
             framebuffer[LY][x_pos+sprite_x].b == display_palette[0].b &&
             framebuffer[LY][x_pos+sprite_x].g == display_palette[0].g))
        {
          framebuffer[LY][x_pos+sprite_x] = display_palette[colour];
        }
      }
    }
  }
}

void Display::update_status()
{
  u8 STAT = memory.get8(Memory::IO::STAT);

  // TODO don't always reset this
  STAT = 252;

  u8 scanline = memory.get8(Memory::IO::LY);
  MODE::Mode mode;

  if (scanline > 144)
  {
    mode = MODE::VBLANK;
  }
  else if (scanline_counter > cycles_per_scanline - 80)
  {
    mode = MODE::OAM;
  }
  else if (scanline_counter > cycles_per_scanline - 80 - 172)
  {
    mode = MODE::VRAM;
  }
  else
  {
    mode = MODE::HBLANK;
  }

  const MODE::Mode current_mode = static_cast<MODE::Mode>(STAT & 0x3);
  if (current_mode != mode &&
      mode != MODE::VRAM &&
      (STAT >> (mode + 3)) & 0x1)
  {
    cpu.raise_interrupt(LR35902::Interrupt::LCD);
  }

  // Update mode
  STAT &= ~0x3;
  STAT |= mode;

  const u8 LYC = memory.get8(Memory::IO::LYC);
  if (LYC == scanline)
  {
    // Set coincidence flag
    STAT |= (1<<2);
    if ((STAT >> 6) & 0x1)
    {
      cpu.raise_interrupt(LR35902::Interrupt::LCD);
    }
  }
  else
  {
    // Clear coincidence flag
    STAT &= ~(1<<2);
  }

  memory.set8(Memory::IO::STAT, STAT);
}
<|endoftext|>


<commit_before>/*
  Q Light Controller Plus
  artnetpacketizer.cpp

  Copyright (c) Massimo Callegari

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include "artnetpacketizer.h"

#include <QStringList>
#include <QDebug>

ArtNetPacketizer::ArtNetPacketizer()
{
    // Initialize a commond header.
    // Changing only the tenth byte will create a valid ArtNet header
    m_commonHeader.clear();
    m_commonHeader.append(ARTNET_CODE_STR);
    m_commonHeader.append('\0');

    // empty opcode
    m_commonHeader.append('\0');
    m_commonHeader.append('\0');

    // version 14 by default
    m_commonHeader.append('\0');
    m_commonHeader.append((char)0x0e);

    m_sequence[0] = 1;
    m_sequence[1] = 1;
    m_sequence[2] = 1;
    m_sequence[3] = 1;
}

/*********************************************************************
 * Sender functions
 *********************************************************************/

void ArtNetPacketizer::setupArtNetPoll(QByteArray& data)
{
    data.clear();
    data.append(m_commonHeader);
    const char opCodeMSB = (ARTNET_POLL >> 8);
    data[9] = opCodeMSB;
    data.append((char)0x02); // TalkToMe
    data.append('\0'); // Priority
}

void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr)
{
    int i = 0;
    data.clear();
    data.append(m_commonHeader);
    data.remove(9, 2);
    const char opCodeMSB = (ARTNET_POLLREPLY >> 8);
    data[9] = opCodeMSB;
    QString ipStr = ipAddr.toString();
    QStringList ipAddrList = ipStr.split(".");
    foreach (QString val, ipAddrList)
        data.append((char)val.toInt()); // IP address[4]
    data.append((char)0x36);     // Port LSB
    data.append((char)0x19);     // Port MSB
    data.append((char)0x04);     // Version MSB
    data.append((char)0x20);     // Version LSB
    data.append((char)0x00);     // Sub Switch MSB
    data.append((char)0x00);     // Sub Switch LSB
    data.append((char)0xFF);     // OEM Value MSB
    data.append((char)0xFF);     // OEM Value LSB
    data.append((char)0x00);     // UBEA version
    data.append((char)0xF0);     // Status1 - Ready and booted
    data.append((char)0xFF);     // ESTA Manufacturer MSB
    data.append((char)0xFF);     // ESTA Manufacturer LSB
    data.append("QLC+");   // Short Name
    for (i = 0; i < 14; i++)
        data.append((char)0x00); // 14 bytes of stuffing
    data.resize(data.length() + 14);
    data.append("Q Light Controller Plus - ArtNet interface"); // Long Name
    for (i = 0; i < 22; i++) // 64-42 bytes of stuffing. 42 is the lenght of the long name
        data.append((char)0x00);
    for (i = 0; i < 64; i++)
        data.append((char)0x00); // Node report
    data.append((char)0x00);     // NumPort MSB
    // FIXME: this should reflect the actual state of QLC+ output ports !
    data.append((char)0x01);     // NumPort LSB
    data.append((char)0x80);     // Port 1 type: can output DMX512 data
    data.append((char)0x80);     // Port 2 type: can output DMX512 data
    data.append((char)0x80);     // Port 3 type: can output DMX512 data
    data.append((char)0x80);     // Port 4 type: can output DMX512 data
    // FIXME: this should reflect the actual state of QLC+ output ports !
    for (i = 0; i < 12; i++)
        data.append((char)0x00); // Set GoodInput[4], GoodOutput[4] and SwIn[4] all to unknown state
    data.append((char)0x00);     // SwOut0 - output 0
    data.append((char)0x01);     // SwOut1 - output 1
    data.append((char)0x02);     // SwOut2 - output 2
    data.append((char)0x03);     // SwOut3 - output 3
    for (i = 0; i < 7; i++)
        data.append((char)0x00);  // SwVideo, SwMacro, SwRemote and 4 spare bytes
    QStringList MAC = MACaddr.split(":");
    foreach (QString couple, MAC)
    {
        bool ok;
        data.append((char)couple.toInt(&ok, 16));
    }
    for (i = 0; i < 32; i++)
        data.append((char)0x00); // bindIp[4], BindIndex, Status2 and filler
}

void ArtNetPacketizer::setupArtNetDmx(QByteArray& data, const int &universe, const QByteArray &values)
{
    data.clear();
    data.append(m_commonHeader);
    const char opCodeMSB = (ARTNET_DMX >> 8);
    data[9] = opCodeMSB;
    data.append(m_sequence[universe]); // Sequence
    data.append('\0'); // Physical
    data.append((char)(universe & 0x00FF));
    data.append((char)(universe >> 8));
    int len = values.length();
    data.append((char)(len >> 8));
    data.append((char)(len & 0x00FF));
    data.append(values);

    if (m_sequence[universe] == 0xff)
        m_sequence[universe] = 1;
    else
        m_sequence[universe]++;
}

/*********************************************************************
 * Receiver functions
 *********************************************************************/

bool ArtNetPacketizer::checkPacketAndCode(QByteArray& data, int &code)
{
    /* An ArtNet header must be at least 12 bytes long */
    if (data.length() < 12)
        return false;

    /* Check "Art-Net" keyword presence */
    if (data.indexOf(ARTNET_CODE_STR) != 0)
        return false;

    if (data.at(7) != 0x00)
        return false;

    code = ((int)data.at(9) << 8) + data.at(8);

    return true;
}

bool ArtNetPacketizer::fillArtPollReplyInfo(QByteArray& data, ArtNetNodeInfo &info)
{
    if (data.isNull())
        return false;

    QByteArray shortName = data.mid(26, 18);
    QByteArray longName = data.mid(44, 64);
    info.shortName = QString(shortName.data()).simplified();
    info.longName = QString(longName.data()).simplified();

    qDebug() << "getArtPollReplyInfo shortName: " << info.shortName;
    qDebug() << "getArtPollReplyInfo longName: " << info.longName;

    return true;
}

bool ArtNetPacketizer::fillDMXdata(QByteArray& data, QByteArray &dmx, quint32 &universe)
{
    if (data.isNull())
        return false;
    dmx.clear();
    //char sequence = data.at(12);
    //qDebug() << "Sequence: " << sequence;
    // phisycal skipped
    universe = (data.at(13) << 8) + data.at(14);
    if (universe > 0)
        universe--;
    // net skipped
    unsigned int msb = (data.at(16)&0xff);
    unsigned int lsb = (data.at(17)&0xff);
    int length = (msb << 8) | lsb;

    //qDebug() << "length: " << length;
    dmx.append(data.mid(18, length));
    return true;
}

<commit_msg>Art-Net: fix input<commit_after>/*
  Q Light Controller Plus
  artnetpacketizer.cpp

  Copyright (c) Massimo Callegari

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include "artnetpacketizer.h"

#include <QStringList>
#include <QDebug>

ArtNetPacketizer::ArtNetPacketizer()
{
    // Initialize a commond header.
    // Changing only the tenth byte will create a valid ArtNet header
    m_commonHeader.clear();
    m_commonHeader.append(ARTNET_CODE_STR);
    m_commonHeader.append('\0');

    // empty opcode
    m_commonHeader.append('\0');
    m_commonHeader.append('\0');

    // version 14 by default
    m_commonHeader.append('\0');
    m_commonHeader.append((char)0x0e);

    m_sequence[0] = 1;
    m_sequence[1] = 1;
    m_sequence[2] = 1;
    m_sequence[3] = 1;
}

/*********************************************************************
 * Sender functions
 *********************************************************************/

void ArtNetPacketizer::setupArtNetPoll(QByteArray& data)
{
    data.clear();
    data.append(m_commonHeader);
    const char opCodeMSB = (ARTNET_POLL >> 8);
    data[9] = opCodeMSB;
    data.append((char)0x02); // TalkToMe
    data.append('\0'); // Priority
}

void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr)
{
    int i = 0;
    data.clear();
    data.append(m_commonHeader);
    data.remove(9, 2);
    const char opCodeMSB = (ARTNET_POLLREPLY >> 8);
    data[9] = opCodeMSB;
    QString ipStr = ipAddr.toString();
    QStringList ipAddrList = ipStr.split(".");
    foreach (QString val, ipAddrList)
        data.append((char)val.toInt()); // IP address[4]
    data.append((char)0x36);     // Port LSB
    data.append((char)0x19);     // Port MSB
    data.append((char)0x04);     // Version MSB
    data.append((char)0x20);     // Version LSB
    data.append((char)0x00);     // Sub Switch MSB
    data.append((char)0x00);     // Sub Switch LSB
    data.append((char)0xFF);     // OEM Value MSB
    data.append((char)0xFF);     // OEM Value LSB
    data.append((char)0x00);     // UBEA version
    data.append((char)0xF0);     // Status1 - Ready and booted
    data.append((char)0xFF);     // ESTA Manufacturer MSB
    data.append((char)0xFF);     // ESTA Manufacturer LSB
    data.append("QLC+");   // Short Name
    for (i = 0; i < 14; i++)
        data.append((char)0x00); // 14 bytes of stuffing
    data.resize(data.length() + 14);
    data.append("Q Light Controller Plus - ArtNet interface"); // Long Name
    for (i = 0; i < 22; i++) // 64-42 bytes of stuffing. 42 is the lenght of the long name
        data.append((char)0x00);
    for (i = 0; i < 64; i++)
        data.append((char)0x00); // Node report
    data.append((char)0x00);     // NumPort MSB
    // FIXME: this should reflect the actual state of QLC+ output ports !
    data.append((char)0x01);     // NumPort LSB
    data.append((char)0x80);     // Port 1 type: can output DMX512 data
    data.append((char)0x80);     // Port 2 type: can output DMX512 data
    data.append((char)0x80);     // Port 3 type: can output DMX512 data
    data.append((char)0x80);     // Port 4 type: can output DMX512 data
    // FIXME: this should reflect the actual state of QLC+ output ports !
    for (i = 0; i < 12; i++)
        data.append((char)0x00); // Set GoodInput[4], GoodOutput[4] and SwIn[4] all to unknown state
    data.append((char)0x00);     // SwOut0 - output 0
    data.append((char)0x01);     // SwOut1 - output 1
    data.append((char)0x02);     // SwOut2 - output 2
    data.append((char)0x03);     // SwOut3 - output 3
    for (i = 0; i < 7; i++)
        data.append((char)0x00);  // SwVideo, SwMacro, SwRemote and 4 spare bytes
    QStringList MAC = MACaddr.split(":");
    foreach (QString couple, MAC)
    {
        bool ok;
        data.append((char)couple.toInt(&ok, 16));
    }
    for (i = 0; i < 32; i++)
        data.append((char)0x00); // bindIp[4], BindIndex, Status2 and filler
}

void ArtNetPacketizer::setupArtNetDmx(QByteArray& data, const int &universe, const QByteArray &values)
{
    data.clear();
    data.append(m_commonHeader);
    const char opCodeMSB = (ARTNET_DMX >> 8);
    data[9] = opCodeMSB;
    data.append(m_sequence[universe]); // Sequence
    data.append('\0'); // Physical
    data.append((char)(universe & 0x00FF));
    data.append((char)(universe >> 8));
    int len = values.length();
    data.append((char)(len >> 8));
    data.append((char)(len & 0x00FF));
    data.append(values);

    if (m_sequence[universe] == 0xff)
        m_sequence[universe] = 1;
    else
        m_sequence[universe]++;
}

/*********************************************************************
 * Receiver functions
 *********************************************************************/

bool ArtNetPacketizer::checkPacketAndCode(QByteArray& data, int &code)
{
    /* An ArtNet header must be at least 12 bytes long */
    if (data.length() < 12)
        return false;

    /* Check "Art-Net" keyword presence */
    if (data.indexOf(ARTNET_CODE_STR) != 0)
        return false;

    if (data.at(7) != 0x00)
        return false;

    code = ((int)data.at(9) << 8) + data.at(8);

    return true;
}

bool ArtNetPacketizer::fillArtPollReplyInfo(QByteArray& data, ArtNetNodeInfo &info)
{
    if (data.isNull())
        return false;

    QByteArray shortName = data.mid(26, 18);
    QByteArray longName = data.mid(44, 64);
    info.shortName = QString(shortName.data()).simplified();
    info.longName = QString(longName.data()).simplified();

    qDebug() << "getArtPollReplyInfo shortName: " << info.shortName;
    qDebug() << "getArtPollReplyInfo longName: " << info.longName;

    return true;
}

bool ArtNetPacketizer::fillDMXdata(QByteArray& data, QByteArray &dmx, quint32 &universe)
{
    if (data.isNull())
        return false;
    dmx.clear();
    //char sequence = data.at(12);
    //qDebug() << "Sequence: " << sequence;
    // physical skipped
    universe = (data.at(14) << 8) + data.at(13);
    if (universe > 0)
        universe--;
    // net skipped
    unsigned int msb = (data.at(16)&0xff);
    unsigned int lsb = (data.at(17)&0xff);
    int length = (msb << 8) | lsb;

    //qDebug() << "length: " << length;
    dmx.append(data.mid(18, length));
    return true;
}

<|endoftext|>


<commit_before>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/memory/linked_ptr.h"
#include "base/message_loop/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "tools/gn/build_settings.h"
#include "tools/gn/err.h"
#include "tools/gn/loader.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/parser.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/tokenizer.h"

namespace {

class MockInputFileManager {
 public:
  typedef base::Callback<void(const ParseNode*)> Callback;

  MockInputFileManager() {
  }

  LoaderImpl::AsyncLoadFileCallback GetCallback();

  // Sets a given response for a given source file.
  void AddCannedResponse(const SourceFile& source_file,
                         const std::string& source);

  // Returns true if there is/are pending load(s) matching the given file(s).
  bool HasOnePending(const SourceFile& f) const;
  bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const;

  void IssueAllPending();

 private:
  struct CannedResult {
    scoped_ptr<InputFile> input_file;
    std::vector<Token> tokens;
    scoped_ptr<ParseNode> root;
  };

  bool AsyncLoadFile(const LocationRange& origin,
                     const BuildSettings* build_settings,
                     const SourceFile& file_name,
                     const Callback& callback,
                     Err* err) {
    pending_.push_back(std::make_pair(file_name, callback));
    return true;
  }

  // Owning pointers.
  typedef std::map<SourceFile, linked_ptr<CannedResult> > CannedResponseMap;
  CannedResponseMap canned_responses_;

  std::vector< std::pair<SourceFile, Callback> > pending_;
};

LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetCallback() {
  return base::Bind(&MockInputFileManager::AsyncLoadFile,
                    base::Unretained(this));
}

// Sets a given response for a given source file.
void MockInputFileManager::AddCannedResponse(const SourceFile& source_file,
                                             const std::string& source) {
  CannedResult* canned = new CannedResult;
  canned->input_file.reset(new InputFile(source_file));
  canned->input_file->SetContents(source);

  // Tokenize.
  Err err;
  canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err);
  EXPECT_FALSE(err.has_error());

  // Parse.
  canned->root = Parser::Parse(canned->tokens, &err).Pass();
  EXPECT_FALSE(err.has_error());

  canned_responses_[source_file] = linked_ptr<CannedResult>(canned);
}

bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
  return pending_.size() == 1u && pending_[0].first == f;
}

bool MockInputFileManager::HasTwoPending(const SourceFile& f1,
                                         const SourceFile& f2) const {
  if (pending_.size() != 2u)
    return false;
  return pending_[0].first == f1 && pending_[1].first == f2;
}

void MockInputFileManager::IssueAllPending() {
  BlockNode block(false);  // Default response.

  for (const auto& cur : pending_) {
    CannedResponseMap::const_iterator found = canned_responses_.find(cur.first);
    if (found == canned_responses_.end())
      cur.second.Run(&block);
    else
      cur.second.Run(found->second->root.get());
  }
  pending_.clear();
}

// LoaderTest ------------------------------------------------------------------

class LoaderTest : public testing::Test {
 public:
  LoaderTest() {
    build_settings_.SetBuildDir(SourceDir("//out/Debug/"));
  }
  virtual ~LoaderTest() {
  }

 protected:
  Scheduler scheduler_;
  BuildSettings build_settings_;
  MockInputFileManager mock_ifm_;
};

}  // namespace

// -----------------------------------------------------------------------------

TEST_F(LoaderTest, Foo) {
  SourceFile build_config("//build/config/BUILDCONFIG.gn");
  build_settings_.set_build_config_file(build_config);

  scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));

  // The default toolchain needs to be set by the build config file.
  mock_ifm_.AddCannedResponse(build_config,
                              "set_default_toolchain(\"//tc:tc\")");

  loader->set_async_load_file(mock_ifm_.GetCallback());

  // Request the root build file be loaded. This should kick off the default
  // build config loading.
  SourceFile root_build("//BUILD.gn");
  loader->Load(root_build, LocationRange(), Label());
  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));

  // Completing the build config load should kick off the root build file load.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();
  EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));

  // Load the root build file.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();

  // Schedule some other file to load in another toolchain.
  Label second_tc(SourceDir("//tc2/"), "tc2");
  SourceFile second_file("//foo/BUILD.gn");
  loader->Load(second_file, LocationRange(), second_tc);
  EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn")));

  // Running the toolchain file should schedule the build config file to load
  // for that toolchain.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();

  // We have to tell it we have a toolchain definition now (normally the
  // builder would do this).
  const Settings* default_settings = loader->GetToolchainSettings(Label());
  Toolchain second_tc_object(default_settings, second_tc);
  loader->ToolchainLoaded(&second_tc_object);
  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));

  // Scheduling a second file to load in that toolchain should not make it
  // pending yet (it's waiting for the build config).
  SourceFile third_file("//bar/BUILD.gn");
  loader->Load(third_file, LocationRange(), second_tc);
  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));

  // Running the build config file should make our third file pending.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();
  EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file));

  EXPECT_FALSE(scheduler_.is_failed());
}
<commit_msg>Remove an empty destructor in tool/gn's LoaderTest.<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/memory/linked_ptr.h"
#include "base/message_loop/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "tools/gn/build_settings.h"
#include "tools/gn/err.h"
#include "tools/gn/loader.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/parser.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/tokenizer.h"

namespace {

class MockInputFileManager {
 public:
  typedef base::Callback<void(const ParseNode*)> Callback;

  MockInputFileManager() {
  }

  LoaderImpl::AsyncLoadFileCallback GetCallback();

  // Sets a given response for a given source file.
  void AddCannedResponse(const SourceFile& source_file,
                         const std::string& source);

  // Returns true if there is/are pending load(s) matching the given file(s).
  bool HasOnePending(const SourceFile& f) const;
  bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const;

  void IssueAllPending();

 private:
  struct CannedResult {
    scoped_ptr<InputFile> input_file;
    std::vector<Token> tokens;
    scoped_ptr<ParseNode> root;
  };

  bool AsyncLoadFile(const LocationRange& origin,
                     const BuildSettings* build_settings,
                     const SourceFile& file_name,
                     const Callback& callback,
                     Err* err) {
    pending_.push_back(std::make_pair(file_name, callback));
    return true;
  }

  // Owning pointers.
  typedef std::map<SourceFile, linked_ptr<CannedResult> > CannedResponseMap;
  CannedResponseMap canned_responses_;

  std::vector< std::pair<SourceFile, Callback> > pending_;
};

LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetCallback() {
  return base::Bind(&MockInputFileManager::AsyncLoadFile,
                    base::Unretained(this));
}

// Sets a given response for a given source file.
void MockInputFileManager::AddCannedResponse(const SourceFile& source_file,
                                             const std::string& source) {
  CannedResult* canned = new CannedResult;
  canned->input_file.reset(new InputFile(source_file));
  canned->input_file->SetContents(source);

  // Tokenize.
  Err err;
  canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err);
  EXPECT_FALSE(err.has_error());

  // Parse.
  canned->root = Parser::Parse(canned->tokens, &err).Pass();
  EXPECT_FALSE(err.has_error());

  canned_responses_[source_file] = linked_ptr<CannedResult>(canned);
}

bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
  return pending_.size() == 1u && pending_[0].first == f;
}

bool MockInputFileManager::HasTwoPending(const SourceFile& f1,
                                         const SourceFile& f2) const {
  if (pending_.size() != 2u)
    return false;
  return pending_[0].first == f1 && pending_[1].first == f2;
}

void MockInputFileManager::IssueAllPending() {
  BlockNode block(false);  // Default response.

  for (const auto& cur : pending_) {
    CannedResponseMap::const_iterator found = canned_responses_.find(cur.first);
    if (found == canned_responses_.end())
      cur.second.Run(&block);
    else
      cur.second.Run(found->second->root.get());
  }
  pending_.clear();
}

// LoaderTest ------------------------------------------------------------------

class LoaderTest : public testing::Test {
 public:
  LoaderTest() {
    build_settings_.SetBuildDir(SourceDir("//out/Debug/"));
  }

 protected:
  Scheduler scheduler_;
  BuildSettings build_settings_;
  MockInputFileManager mock_ifm_;
};

}  // namespace

// -----------------------------------------------------------------------------

TEST_F(LoaderTest, Foo) {
  SourceFile build_config("//build/config/BUILDCONFIG.gn");
  build_settings_.set_build_config_file(build_config);

  scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));

  // The default toolchain needs to be set by the build config file.
  mock_ifm_.AddCannedResponse(build_config,
                              "set_default_toolchain(\"//tc:tc\")");

  loader->set_async_load_file(mock_ifm_.GetCallback());

  // Request the root build file be loaded. This should kick off the default
  // build config loading.
  SourceFile root_build("//BUILD.gn");
  loader->Load(root_build, LocationRange(), Label());
  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));

  // Completing the build config load should kick off the root build file load.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();
  EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));

  // Load the root build file.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();

  // Schedule some other file to load in another toolchain.
  Label second_tc(SourceDir("//tc2/"), "tc2");
  SourceFile second_file("//foo/BUILD.gn");
  loader->Load(second_file, LocationRange(), second_tc);
  EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn")));

  // Running the toolchain file should schedule the build config file to load
  // for that toolchain.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();

  // We have to tell it we have a toolchain definition now (normally the
  // builder would do this).
  const Settings* default_settings = loader->GetToolchainSettings(Label());
  Toolchain second_tc_object(default_settings, second_tc);
  loader->ToolchainLoaded(&second_tc_object);
  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));

  // Scheduling a second file to load in that toolchain should not make it
  // pending yet (it's waiting for the build config).
  SourceFile third_file("//bar/BUILD.gn");
  loader->Load(third_file, LocationRange(), second_tc);
  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));

  // Running the build config file should make our third file pending.
  mock_ifm_.IssueAllPending();
  scheduler_.main_loop()->RunUntilIdle();
  EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file));

  EXPECT_FALSE(scheduler_.is_failed());
}
<|endoftext|>


<commit_before>
/*!
	\file

	\author Igor Mironchik (igor.mironchik at gmail dot com).

	Copyright (c) 2017 Igor Mironchik

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
*/

// cfgfile include.
#include <cfgfile/utils.hpp>

// generator_t include.
#include "generator.hpp"

// C++ include.
#include <fstream>

// Args include.
#include <Args/all.hpp>

//
// for_generation_t
//

//! Data uses to generate.
class for_generation_t {
public:
	for_generation_t()
	{
	}

	for_generation_t( const std::string & input_file, const std::string & output_file )
		:	m_input_file_name( input_file )
		,	m_output_file_name( output_file )
	{
	}

	~for_generation_t()
	{
	}

	for_generation_t( const for_generation_t & other )
		:	m_input_file_name( other.input_file() )
		,	m_output_file_name( other.output_file() )
	{
	}

	for_generation_t & operator = ( const for_generation_t & other )
	{
		if( this != &other )
		{
			m_input_file_name = other.input_file();
			m_output_file_name = other.output_file();
		}

		return *this;
	}

	//! \return Input file name.
	const std::string & input_file() const
	{
		return m_input_file_name;
	}

	//! \return Output file name.
	const std::string & output_file() const
	{
		return m_output_file_name;
	}

private:
	//! Input file name.
	std::string m_input_file_name;
	//! Output file name.
	std::string m_output_file_name;
}; // class for_generation_t

//
// parse_cli
//

static inline for_generation_t parse_cli( int argc, char ** argv )
{
	Args::Arg input( 'i', "input", true, true );
	input.setDescription( "Input file name" );
	Args::Arg output( 'o', "output", true, true );
	output.setDescription( "Output file name" );

	Args::CmdLine cmd( argc, argv );

	Args::Help help;
	help.setAppDescription( "C++ header generator for cfgfile." );
	help.setExecutable( argv[ 0 ] );

	cmd.addArg( input );
	cmd.addArg( output );
	cmd.addArg( help );

	cmd.parse();

	for_generation_t data( input.value(), output.value() );

	if( data.input_file().empty() )
	{
		std::cout << "Please specify input file." << std::endl;

		exit( 1 );
	}

	if( data.output_file().empty() )
	{
		std::cout << "Please specify output file." << std::endl;

		exit( 1 );
	}

	return data;
}

int main( int argc, char ** argv )
{
	for_generation_t data;

	try {
		data = parse_cli( argc, argv );
	}
	catch( const Args::HelpHasBeenPrintedException & )
	{
		return 0;
	}
	catch( const Args::BaseException & x )
	{
		std::cout << x.desc() << std::endl;

		return 1;
	}

	std::ifstream in( data.input_file() );

	if( !in.good() )
	{
		std::cout << "Specified input file doesn't exist." << std::endl;

		exit( 1 );
	}

	cfgfile::generator::cfg::model_t model;

	try {
		cfgfile::generator::cfg::tag_model_t tag;

		cfgfile::read_cfgfile( tag, in, data.input_file() );

		in.close();

		model = tag.cfg();

		model.prepare();

		model.check();
	}
	catch( const cfgfile::exception_t< cfgfile::string_trait_t > & x )
	{
		in.close();

		std::cout << x.desc() << std::endl;

		return 1;
	}

	std::ofstream out( data.output_file() );

	if( out.good() )
	{
		cfgfile::generator::cpp_generator_t gen( model );

		gen.generate( out );

		out.close();

		return 0;
	}
	else
	{
		std::cout << "Couldn't open output file for writting."
			<< std::endl;

		return 1;
	}
}
<commit_msg>Updated Args.<commit_after>
/*!
	\file

	\author Igor Mironchik (igor.mironchik at gmail dot com).

	Copyright (c) 2017 Igor Mironchik

	Permission is hereby granted, free of charge, to any person
	obtaining a copy of this software and associated documentation
	files (the "Software"), to deal in the Software without
	restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the
	Software is furnished to do so, subject to the following
	conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	OTHER DEALINGS IN THE SOFTWARE.
*/

// cfgfile include.
#include <cfgfile/utils.hpp>

// generator_t include.
#include "generator.hpp"

// C++ include.
#include <fstream>

// Args include.
#include <Args/all.hpp>

//
// for_generation_t
//

//! Data uses to generate.
class for_generation_t {
public:
	for_generation_t()
	{
	}

	for_generation_t( const std::string & input_file, const std::string & output_file )
		:	m_input_file_name( input_file )
		,	m_output_file_name( output_file )
	{
	}

	~for_generation_t()
	{
	}

	for_generation_t( const for_generation_t & other )
		:	m_input_file_name( other.input_file() )
		,	m_output_file_name( other.output_file() )
	{
	}

	for_generation_t & operator = ( const for_generation_t & other )
	{
		if( this != &other )
		{
			m_input_file_name = other.input_file();
			m_output_file_name = other.output_file();
		}

		return *this;
	}

	//! \return Input file name.
	const std::string & input_file() const
	{
		return m_input_file_name;
	}

	//! \return Output file name.
	const std::string & output_file() const
	{
		return m_output_file_name;
	}

private:
	//! Input file name.
	std::string m_input_file_name;
	//! Output file name.
	std::string m_output_file_name;
}; // class for_generation_t

//
// parse_cli
//

static inline for_generation_t parse_cli( int argc, char ** argv )
{
	Args::CmdLine cmd;

	cmd.addArgWithFlagAndName( 'i', "input", true, true, "Input file name" )
		.addArgWithFlagAndName( 'o', "output", true, true, "Output file name" )
		.addHelp( true, argv[ 0 ], "C++ header generator for cfgfile." );

	cmd.parse( argc, argv );

	for_generation_t data( cmd.value( "-i" ), cmd.value( "-o" ) );

	if( data.input_file().empty() )
	{
		std::cout << "Please specify input file." << std::endl;

		exit( 1 );
	}

	if( data.output_file().empty() )
	{
		std::cout << "Please specify output file." << std::endl;

		exit( 1 );
	}

	return data;
}

int main( int argc, char ** argv )
{
	for_generation_t data;

	try {
		data = parse_cli( argc, argv );
	}
	catch( const Args::HelpHasBeenPrintedException & )
	{
		return 0;
	}
	catch( const Args::BaseException & x )
	{
		std::cout << x.desc() << std::endl;

		return 1;
	}

	std::ifstream in( data.input_file() );

	if( !in.good() )
	{
		std::cout << "Specified input file doesn't exist." << std::endl;

		exit( 1 );
	}

	cfgfile::generator::cfg::model_t model;

	try {
		cfgfile::generator::cfg::tag_model_t tag;

		cfgfile::read_cfgfile( tag, in, data.input_file() );

		in.close();

		model = tag.cfg();

		model.prepare();

		model.check();
	}
	catch( const cfgfile::exception_t< cfgfile::string_trait_t > & x )
	{
		in.close();

		std::cout << x.desc() << std::endl;

		return 1;
	}

	std::ofstream out( data.output_file() );

	if( out.good() )
	{
		cfgfile::generator::cpp_generator_t gen( model );

		gen.generate( out );

		out.close();

		return 0;
	}
	else
	{
		std::cout << "Couldn't open output file for writting."
			<< std::endl;

		return 1;
	}
}
<|endoftext|>


<commit_before>
#include <string>
#include <sstream>
#include <fstream>
#include <iterator>
#include <vector>
#include <map>
#include <utility>
#include <functional>
#include <algorithm>
#include <limits>

#include <time.h>

#include <omp.h>
#include <boost/program_options.hpp>

#include "tools.hpp"

namespace b_po = boost::program_options;

// 3-column float with col1 = x-val, col2 = y-val, col3 = density
// addressed by [row*3+col] with n_rows = n_bins^2
std::vector<float> calculate_density_histogram(const std::vector<float>& dens,
                                               const std::string& projections,
                                               std::pair<std::size_t, std::size_t> dims,
                                               std::size_t n_bins) {
  auto coords_tuple = read_coords<float>(projections, {dims.first, dims.second});
  std::vector<float> coords = std::get<0>(coords_tuple);
  std::size_t n_rows = std::get<1>(coords_tuple);

  float x_min = std::numeric_limits<float>::max();
  float x_max = std::numeric_limits<float>::min();
  float y_min = std::numeric_limits<float>::max();
  float y_max = std::numeric_limits<float>::min();

  auto X = [&](std::size_t i) { return coords[i*2]; };
  auto Y = [&](std::size_t i) { return coords[i*2 + 1]; };
  for (std::size_t i=0; i < n_rows; ++i) {
    auto mm = std::minmax({X(i), x_min, x_max});
    x_min = mm.first;
    x_max = mm.second;
    mm = std::minmax({Y(i), y_min, y_max});
    y_min = mm.first;
    y_max = mm.second;
  }

  float dx = (x_max - x_min) / n_bins;
  float dy = (y_max - y_min) / n_bins;

  // setup bins
  std::vector<float> hist(3*n_bins*n_bins);
  // addressing shortcuts
  auto HX = [&](std::size_t i, std::size_t j) -> float& {return hist[(i*n_bins+j)*3];};
  auto HY = [&](std::size_t i, std::size_t j) -> float& {return hist[(i*n_bins+j)*3+1];};
  auto HZ = [&](std::size_t i, std::size_t j) -> float& {return hist[(i*n_bins+j)*3+2];};
  for (std::size_t i=0; i < n_bins; ++i) {
    for (std::size_t j=0; j < n_bins; ++j) {
      HX(i,j) = x_min + i*dx;
      HY(i,j) = y_min + j*dy;
    }
  }
  // sort data into bins
  for (std::size_t i_frame=0; i_frame < n_rows; ++i_frame) {
    std::size_t i_xbin = n_bins-1;
    std::size_t i_ybin = n_bins-1;
    // find index for x-bin
    for (std::size_t ix=0; ix < n_bins-1; ++ix) {
      float x_val = coords[i_frame*2];
      if (HX(ix,0) < x_val && x_val < HX(ix+1,0)) {
        i_xbin = ix;
        break;
      }
    }
    // find index for y-bin
    for (std::size_t iy=0; iy < n_bins-1; ++iy) {
      float y_val = coords[i_frame*2+1];
      if (HY(i_xbin,iy) < y_val && y_val < HY(i_xbin,iy+1)) {
        i_ybin = iy;
        break;
      }
    }
    // add density to histogram
    HZ(i_xbin, i_ybin) += dens[i_frame];
  }
  return hist;
}

std::vector<float> calculate_densities(const std::vector<std::size_t>& pops) {
  const std::size_t n_frames = pops.size();
  std::vector<float> dens(n_frames);
  float max_pop = (float) ( * std::max_element(pops.begin(), pops.end()));
  for (std::size_t i=0; i < n_frames; ++i) {
    dens[i] = (float) pops[i] / max_pop;
  }
  return dens;
}

std::vector<std::size_t> calculate_populations(const std::string& neighborhood, const std::string& projections) {
  std::size_t n_frames = 0;
  // read projections linewise to determine number of frames
  {
    std::string line;
    std::ifstream ifs(projections);
    while (ifs.good()) {
      std::getline(ifs, line);
      if ( ! line.empty()) {
        ++n_frames;
      }
    }
  }
  // set initial population = 1 for every frame
  // (i.e. the frame itself is in population)
  std::vector<std::size_t> pops(n_frames, 1);
  // read neighborhood info and calculate populations
  {
    std::ifstream ifs(neighborhood);
    std::size_t buf;
    while (ifs.good()) {
      // from
      ifs >> buf;
      ++pops[buf];
      // to
      ifs >> buf;
      ++pops[buf];
      // next line
      ifs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
  }
  return pops;
}

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

  b_po::variables_map var_map;
  b_po::options_description desc (std::string(argv[0]).append(
    "\n\n"
    "output: populations of frame environment\n"
    "\n"
    "options"));
  desc.add_options()
    ("help,h", "show this help")
    ("input,i", b_po::value<std::string>()->required(), "projected data")
    ("neighborhood,N", b_po::value<std::string>()->required(), "neighborhood info")
    ("population,p", b_po::bool_switch()->default_value(false), "print populations instead of densities");
  try {
    b_po::positional_options_description p;
    p.add("input", -1);
    b_po::store(b_po::command_line_parser(argc, argv).options(desc).positional(p).run(), var_map);
    b_po::notify(var_map);
  } catch (b_po::error& e) {
    if ( ! var_map.count("help")) {
      std::cout << "\n" << e.what() << "\n\n" << std::endl;
    }
    std::cout << desc << std::endl;
    return 2;
  }

  if (var_map.count("help")) {
    std::cout << desc << std::endl;
    return 1;
  }

  ////

  //std::time_t start, finish;
  //time(&start);

  std::vector<std::size_t> pops = calculate_populations(var_map["neighborhood"].as<std::string>(),
                                                        var_map["input"].as<std::string>());

  //time(&finish);
  //double elapsed = difftime(finish, start);
  //std::cerr << "time for neighborhood search [s]: " << elapsed << std::endl;

  if (var_map["population"].as<bool>()) {
    for (std::size_t i=0; i < pops.size(); ++i) {
      std::cout << i << " " << pops[i] << "\n";
    }
  } else {
    std::vector<float> dens = calculate_densities(pops);
    for (std::size_t i=0; i < dens.size(); ++i) {
      std::cout << i << " " << dens[i] << "\n";
    }
  }

  return 0;
}

<commit_msg>finished histogram (untested)<commit_after>
#include <string>
#include <sstream>
#include <fstream>
#include <iterator>
#include <vector>
#include <map>
#include <utility>
#include <functional>
#include <algorithm>
#include <limits>

#include <time.h>

#include <omp.h>
#include <boost/program_options.hpp>

#include "tools.hpp"

namespace b_po = boost::program_options;

// 3-column float with col1 = x-val, col2 = y-val, col3 = density
// addressed by [row*3+col] with n_rows = n_bins^2
std::vector<float> calculate_density_histogram(const std::vector<float>& dens,
                                               const std::string& projections,
                                               std::pair<std::size_t, std::size_t> dims,
                                               std::size_t n_bins) {
  auto coords_tuple = read_coords<float>(projections, {dims.first, dims.second});
  std::vector<float> coords = std::get<0>(coords_tuple);
  std::size_t n_rows = std::get<1>(coords_tuple);

  float x_min = std::numeric_limits<float>::max();
  float x_max = std::numeric_limits<float>::min();
  float y_min = std::numeric_limits<float>::max();
  float y_max = std::numeric_limits<float>::min();

  auto X = [&](std::size_t i) { return coords[i*2]; };
  auto Y = [&](std::size_t i) { return coords[i*2 + 1]; };
  for (std::size_t i=0; i < n_rows; ++i) {
    auto mm = std::minmax({X(i), x_min, x_max});
    x_min = mm.first;
    x_max = mm.second;
    mm = std::minmax({Y(i), y_min, y_max});
    y_min = mm.first;
    y_max = mm.second;
  }

  float dx = (x_max - x_min) / n_bins;
  float dy = (y_max - y_min) / n_bins;

  // setup bins
  std::vector<float> hist(3*n_bins*n_bins);
  // addressing shortcuts
  auto HX = [&](std::size_t i, std::size_t j) -> float& {return hist[(i*n_bins+j)*3];};
  auto HY = [&](std::size_t i, std::size_t j) -> float& {return hist[(i*n_bins+j)*3+1];};
  auto HZ = [&](std::size_t i, std::size_t j) -> float& {return hist[(i*n_bins+j)*3+2];};
  for (std::size_t i=0; i < n_bins; ++i) {
    for (std::size_t j=0; j < n_bins; ++j) {
      HX(i,j) = x_min + i*dx;
      HY(i,j) = y_min + j*dy;
    }
  }
  // sort data into bins
  for (std::size_t i_frame=0; i_frame < n_rows; ++i_frame) {
    std::size_t i_xbin = n_bins-1;
    std::size_t i_ybin = n_bins-1;
    // find index for x-bin
    for (std::size_t ix=0; ix < n_bins-1; ++ix) {
      float x_val = coords[i_frame*2];
      if (HX(ix,0) < x_val && x_val < HX(ix+1,0)) {
        i_xbin = ix;
        break;
      }
    }
    // find index for y-bin
    for (std::size_t iy=0; iy < n_bins-1; ++iy) {
      float y_val = coords[i_frame*2+1];
      if (HY(i_xbin,iy) < y_val && y_val < HY(i_xbin,iy+1)) {
        i_ybin = iy;
        break;
      }
    }
    // add density to histogram
    HZ(i_xbin, i_ybin) += dens[i_frame];
  }
  return hist;
}

std::vector<float> calculate_densities(const std::vector<std::size_t>& pops) {
  const std::size_t n_frames = pops.size();
  std::vector<float> dens(n_frames);
  float max_pop = (float) ( * std::max_element(pops.begin(), pops.end()));
  for (std::size_t i=0; i < n_frames; ++i) {
    dens[i] = (float) pops[i] / max_pop;
  }
  return dens;
}

std::vector<std::size_t> calculate_populations(const std::string& neighborhood, const std::string& projections) {
  std::size_t n_frames = 0;
  // read projections linewise to determine number of frames
  {
    std::string line;
    std::ifstream ifs(projections);
    while (ifs.good()) {
      std::getline(ifs, line);
      if ( ! line.empty()) {
        ++n_frames;
      }
    }
  }
  // set initial population = 1 for every frame
  // (i.e. the frame itself is in population)
  std::vector<std::size_t> pops(n_frames, 1);
  // read neighborhood info and calculate populations
  {
    std::ifstream ifs(neighborhood);
    std::size_t buf;
    while (ifs.good()) {
      // from
      ifs >> buf;
      ++pops[buf];
      // to
      ifs >> buf;
      ++pops[buf];
      // next line
      ifs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
  }
  return pops;
}

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

  b_po::variables_map var_map;
  b_po::options_description desc (std::string(argv[0]).append(
    "\n\n"
    "output: populations of frame environment\n"
    "\n"
    "options"));
  desc.add_options()
    ("help,h", "show this help")
    ("input,i", b_po::value<std::string>()->required(), "projected data")
    ("neighborhood,N", b_po::value<std::string>()->required(), "neighborhood info")
    ("population,p", b_po::bool_switch()->default_value(false), "print populations instead of densities")
    ("histogram,H", b_po::bool_switch()->default_value(false), "print histogram data for densities")
    ("nbins", b_po::value<int>()->default_value(200), "#bins for histogram (default: 200)");
  try {
    b_po::positional_options_description p;
    p.add("input", -1);
    b_po::store(b_po::command_line_parser(argc, argv).options(desc).positional(p).run(), var_map);
    b_po::notify(var_map);
  } catch (b_po::error& e) {
    if ( ! var_map.count("help")) {
      std::cout << "\n" << e.what() << "\n\n" << std::endl;
    }
    std::cout << desc << std::endl;
    return 2;
  }

  if (var_map.count("help")) {
    std::cout << desc << std::endl;
    return 1;
  }

  ////

  //std::time_t start, finish;
  //time(&start);

  std::vector<std::size_t> pops = calculate_populations(var_map["neighborhood"].as<std::string>(),
                                                        var_map["input"].as<std::string>());

  //time(&finish);
  //double elapsed = difftime(finish, start);
  //std::cerr << "time for neighborhood search [s]: " << elapsed << std::endl;

  if (var_map["population"].as<bool>()) {
    for (std::size_t i=0; i < pops.size(); ++i) {
      std::cout << i << " " << pops[i] << "\n";
    }
  } else if (var_map["histogram"].as<bool>()) {
    std::vector<float> dens = calculate_densities(pops);
    int n_bins = var_map["nbins"].as<int>();
    std::vector<float> hist = calculate_density_histogram(dens,
                                                          var_map["input"].as<std::string>(),
                                                          {0,1},
                                                          n_bins);
    for (std::size_t i=0; i < hist.size() / 3; ++i) {
      std::cout << hist[i*3] << " " << hist[i*3+1] << " " << hist[i*3+2] << "\n";
    }
  } else {
    std::vector<float> dens = calculate_densities(pops);
    for (std::size_t i=0; i < dens.size(); ++i) {
      std::cout << i << " " << dens[i] << "\n";
    }
  }

  return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <clocale>
#include <cstring>
#include <cwchar>
#include <fcntl.h>
#include <unistd.h>

const size_t BUFFSIZE = 1024;

int main(const int argc, const char * argv []) {
  std::setlocale(LC_ALL, "en_US.utf8");       // enable unicode
  const char * filename = argv[1];            // filename
  char buffer [BUFFSIZE];                     // read buffer
  int fd = open(filename, O_RDONLY);          // file descriptor
  int nbytes = 0;                             // # bytes read
  int chars = 0;                              // # characters read
  while ((nbytes = read(fd, buffer, BUFFSIZE)) > 0) {
    std::mbstate_t state = std::mbstate_t();  // initialize state
    char * ptr = buffer;                      // start of sliding window
    const char* end = ptr + std::strlen(ptr); // end of sliding window
    int len = 0;                              // # bytes processed
    wchar_t wc = '\0';                        // multibyte character
    while((len = std::mbrtowc(&wc, ptr, end-ptr, &state)) > 0) {
      std::wcout << "Next " << len << " bytes are the character " << wc << std::endl;
      ptr += len;                             // move onto the next one
      chars += 1;                             // increment character count
      if (ptr > end) break;                   // don't go past the end
    } // while
  } // while
  std::wcout << "# chars = " << chars << std::endl;
  close(fd);
} // main

<commit_msg>added todo<commit_after>#include <iostream>
#include <clocale>
#include <cstring>
#include <cwchar>
#include <fcntl.h>
#include <unistd.h>

const size_t BUFFSIZE = 1024;

int main(const int argc, const char * argv []) {
  std::setlocale(LC_ALL, "en_US.utf8");       // enable unicode
  const char * filename = argv[1];            // filename
  char buffer [BUFFSIZE];                     // read buffer
  int fd = open(filename, O_RDONLY);          // file descriptor
  int nbytes = 0;                             // # bytes read
  int chars = 0;                              // # characters read
  while ((nbytes = read(fd, buffer, BUFFSIZE)) > 0) {
    std::mbstate_t state = std::mbstate_t();  // initialize state
    char * ptr = buffer;                      // start of sliding window
    const char* end = ptr + std::strlen(ptr); // end of sliding window
    int len = 0;                              // # bytes processed
    wchar_t wc = '\0';                        // multibyte character
    while((len = std::mbrtowc(&wc, ptr, end-ptr, &state)) > 0) {
      std::wcout << "Next " << len << " bytes are the character " << wc << std::endl;
      ptr += len;                             // move onto the next one
      chars += 1;                             // increment character count
      if (ptr > end) break;                   // don't go past the end
    } // while
    // @TODO What if we're in the middle of a character at the end of this read?
  } // while
  std::wcout << "# chars = " << chars << std::endl;
  close(fd);
} // main

<|endoftext|>